├── README.md ├── LICENSE └── calendar ├── plugins ├── ForgottenPlugin.js ├── GregorianPlugin.js ├── GregorianPTPlugin ├── ArtonPlugin.js └── GolarionPlugin.js ├── ForgottenPlugin.js ├── README.md └── AdvancedCalendar.js /README.md: -------------------------------------------------------------------------------- 1 | # roll20scripts 2 | My scripts developted for roll20 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Caio César Viel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /calendar/plugins/ForgottenPlugin.js: -------------------------------------------------------------------------------- 1 | var ForgottenCalendar = ForgottenCalendar || { 2 | name: "Forgotten Relms Calendar", 3 | 4 | startingDate : { 5 | day: 30, 6 | month: 12, 7 | year: 1489 8 | }, 9 | 10 | setting : { 11 | daysOfYear : 360, 12 | daysOfTheWeek: [ 13 | '1', 14 | '2', 15 | '3', 16 | '4', 17 | '5', 18 | '6', 19 | '7', 20 | '8', 21 | '9', 22 | '10'], 23 | monthsOfTheYear: [ 24 | {name:'Hammer', days: 30, season: 3}, 25 | {name:'Alturiak', days: 30, season: 3}, 26 | {name:'Ches', days: 30, season: 0}, 27 | {name:'Tarsakh', days: 30, season: 0}, 28 | {name:'Mirtul', days: 30, season: 0}, 29 | {name:'Kythorn', days: 30, season: 1}, 30 | {name:'Flamerule', days: 30, season: 1}, 31 | {name:'Eleasis', days: 30, season: 1}, 32 | {name:'Eleint', days: 30, season: 2}, 33 | {name:'Marpenoth', days: 30, season: 2}, 34 | {name:'Uktar', days: 30, season: 2}, 35 | {name:'Nightal', days: 30, season: 3} 36 | ], 37 | yearPrefix: 'DR', 38 | seasons : [ 39 | "Spring", 40 | "Summer", 41 | "Fall", 42 | "Winter" 43 | ], 44 | 45 | }, 46 | 47 | holidays : { 48 | 49 | } 50 | 51 | }; 52 | 53 | 54 | CALENDAR = ForgottenCalendar; -------------------------------------------------------------------------------- /calendar/plugins/GregorianPlugin.js: -------------------------------------------------------------------------------- 1 | var GregorianCalendar = GregorianCalendar || { 2 | name: "Grecorian Calendar", 3 | 4 | startingDate : { 5 | day: 11, 6 | month: 8, 7 | year: 2016 8 | }, 9 | 10 | setting : { 11 | daysOfYear : 365, 12 | daysOfTheWeek: [ 13 | 'Sunday', 14 | 'Monday', 15 | 'Tuesday', 16 | 'Wednesday', 17 | 'Thursday', 18 | 'Friday', 19 | 'Saturday' 20 | ], 21 | monthsOfTheYear: [ 22 | {name:'January', days: 31, season: Season.WINTER}, 23 | {name:'February', days: 28, season: Season.WINTER}, 24 | {name:'March', days: 31, season: Season.SPRING}, 25 | {name:'April', days: 30, season: Season.SPRING}, 26 | {name:'May', days: 31, season: Season.SPRING}, 27 | {name:'June', days: 30, season: Season.SUMMER}, 28 | {name:'July', days: 31, season: Season.SUMMER}, 29 | {name:'August', days: 31, season: Season.SUMMER}, 30 | {name:'September', days: 30, season: Season.FALL}, 31 | {name:'October', days: 31, season: Season.FALL}, 32 | {name:'November', days: 30, season: Season.FALL}, 33 | {name:'December', days: 31, season: Season.WINTER} 34 | ], 35 | yearPrefix: 'AD', 36 | seasons : [ 37 | "Spring", 38 | "Summer", 39 | "Fall", 40 | "Winter" 41 | ], 42 | } 43 | 44 | }; 45 | 46 | CALENDAR = GregorianCalendar; -------------------------------------------------------------------------------- /calendar/plugins/GregorianPTPlugin: -------------------------------------------------------------------------------- 1 | var GregorianCalendarPT = GregorianCalendarPT || { 2 | name: "Calendário Gregoriano", 3 | 4 | startingDate : { 5 | day: 11, 6 | month: 8, 7 | year: 2016 8 | }, 9 | 10 | setting : { 11 | daysOfYear : 365, 12 | daysOfTheWeek: [ 13 | 'Domingo', 14 | 'Segunda-Feira', 15 | 'Terça-Feira', 16 | 'Quarta-Feira', 17 | 'Quinta-Feira', 18 | 'Sexta-Feira', 19 | 'Sábado' 20 | ], 21 | monthsOfTheYear: [ 22 | {name:'Janeiro', days: 31, season: Season.WINTER}, 23 | {name:'Fevereiro', days: 28, season: Season.WINTER}, 24 | {name:'Março', days: 31, season: Season.SPRING}, 25 | {name:'Abril', days: 30, season: Season.SPRING}, 26 | {name:'Maio', days: 31, season: Season.SPRING}, 27 | {name:'Junho', days: 30, season: Season.SUMMER}, 28 | {name:'Julho', days: 31, season: Season.SUMMER}, 29 | {name:'Agosto', days: 31, season: Season.SUMMER}, 30 | {name:'Setembro', days: 30, season: Season.FALL}, 31 | {name:'Outubro', days: 31, season: Season.FALL}, 32 | {name:'Novembro', days: 30, season: Season.FALL}, 33 | {name:'Dezembro', days: 31, season: Season.WINTER} 34 | ], 35 | yearPrefix: 'DC', 36 | seasons : [ 37 | "Primavera", 38 | "Verão", 39 | "Outono", 40 | "Inverno" 41 | ], 42 | } 43 | 44 | }; 45 | 46 | CALENDAR = GregorianCalendarPT; -------------------------------------------------------------------------------- /calendar/plugins/ArtonPlugin.js: -------------------------------------------------------------------------------- 1 | var ArtonCalendar = ArtonCalendar || { 2 | 3 | name: "Calendário Artoniano", 4 | 5 | startingDate : { 6 | day: 7, 7 | month: 2, 8 | year: 1400 9 | }, 10 | 11 | setting : { 12 | daysOfYear : 365, 13 | daysOfTheWeek: [ 14 | 'Aztag', 15 | 'Lanag', 16 | 'Tirag', 17 | 'Jetag', 18 | 'Morag', 19 | 'Kalag', 20 | 'Valag'], 21 | monthsOfTheYear: [ 22 | {name:'Altossol', days: 30, season: 1}, 23 | {name:'Wynn', days: 30, season: 1}, 24 | {name:'Cyd', days: 30, season: 1}, 25 | {name:'Salizz', days: 30, season: 2}, 26 | {name:'Terraviva', days: 30, season: 2}, 27 | {name:'Dantal', days: 30, season: 2}, 28 | {name:'Luvitas', days: 30, season: 3}, 29 | {name:'Weez', days: 30, season: 3}, 30 | {name:'Exinn', days: 30, season: 3}, 31 | {name:'Lunaluz', days: 30, season: 0}, 32 | {name:'Pace', days: 30, season: 0}, 33 | {name:'Aurea', days: 30, season: 0} 34 | ], 35 | yearPrefix: 'CA', 36 | seasons : [ 37 | "Primavera", 38 | "Verão", 39 | "Outono", 40 | "Inverno" 41 | ], 42 | 43 | }, 44 | 45 | holidays : { 46 | "9/2" : [ 47 | { 48 | name: "Dia do Arcano", 49 | area: "", 50 | description: "Todos os magos de uma localidade reúnem-se em uma grande confraternização no dia do Arcano. " 51 | +"Não-magos evitam participar (ou mesmo se aproximar!) desses encontros, mas eles não são de modo " 52 | +"algum secretos. Conta-se que a maioria das pessoas nascidas nesse dia tem uma grande aptidão para " 53 | +"a magia. O dia também é consagrado aos seis gênios elementais que dão os nomes às suas respectivas" 54 | +"sub-raças: Asura, Dao, Div, Djinn, Efreet e Marid" 55 | }, 56 | ], 57 | "12/2" : [ 58 | { 59 | name: " Cerimônia de Admissão da Ordem da Luz referente ao verão", 60 | area: "", 61 | description: "" 62 | }, 63 | ], 64 | }, 65 | }; 66 | 67 | CALENDAR = ArtonCalendar; -------------------------------------------------------------------------------- /calendar/ForgottenPlugin.js: -------------------------------------------------------------------------------- 1 | var ForgottenCalendar = ForgottenCalendar || { 2 | name: "Forgotten Relms Calendar", 3 | 4 | startingDate : { 5 | day: 30, 6 | month: 12, 7 | year: 1489 8 | }, 9 | 10 | setting : { 11 | daysOfYear : 360, 12 | daysOfTheWeek: [ 13 | '1', 14 | '2', 15 | '3', 16 | '4', 17 | '5', 18 | '6', 19 | '7', 20 | '8', 21 | '9', 22 | '10'], 23 | monthsOfTheYear: [ 24 | {name:'Hammer', days: 30, season: 3}, 25 | {name:'Alturiak', days: 30, season: 3}, 26 | {name:'Ches', days: 30, season: 0}, 27 | {name:'Tarsakh', days: 30, season: 0}, 28 | {name:'Mirtul', days: 30, season: 0}, 29 | {name:'Kythorn', days: 30, season: 1}, 30 | {name:'Flamerule', days: 30, season: 1}, 31 | {name:'Eleasis', days: 30, season: 1}, 32 | {name:'Eleint', days: 30, season: 2}, 33 | {name:'Marpenoth', days: 30, season: 2}, 34 | {name:'Uktar', days: 30, season: 2}, 35 | {name:'Nightal', days: 30, season: 3} 36 | ], 37 | yearPrefix: 'DR', 38 | seasons : [ 39 | "Spring", 40 | "Summer", 41 | "Fall", 42 | "Winter" 43 | ], 44 | 45 | }, 46 | 47 | holidays : { 48 | "1/1" : [ 49 | { 50 | name: "Seeing Justice", 51 | area: "Tyr", 52 | description: "" 53 | }, 54 | { 55 | name: "Thunder Blessing", 56 | area: "Moradin", 57 | description: "" 58 | } 59 | ], 60 | "10/1" : [ 61 | { 62 | name: "Great Clang", 63 | area: "Gaerdal", 64 | description: "" 65 | } 66 | ], 67 | "1/2" : [ 68 | { 69 | name: "Seeing Justice", 70 | area: "Tyr", 71 | description: "" 72 | } 73 | ], 74 | "1/3" : [ 75 | { 76 | name: "Seeing Justice", 77 | area: "Tyr", 78 | description: "" 79 | } 80 | ], 81 | "1/4" : [ 82 | { 83 | name: "Queen's Gambit", 84 | area: "Red Knight", 85 | description: "" 86 | } 87 | ], 88 | "1/5" : [ 89 | { 90 | name: "Unwrapping", 91 | area: "Geb", 92 | description: "" 93 | } 94 | ] 95 | } 96 | 97 | 98 | }; 99 | 100 | 101 | CALENDAR = ForgottenCalendar; -------------------------------------------------------------------------------- /calendar/README.md: -------------------------------------------------------------------------------- 1 | #Advanced Calendar 2 | 3 | This script allows the use of non-homogeneous calendars in Roll20. With this script, months with different number of days, like golarions's calendar or our own gregorian's calendar, are possible. However I haven't bothered myself with leap years. 4 | 5 | It has support for weather information. I've used the weather generation rules from Pathfinder, which considers the season (Winter, Summer, Fall and Spring) and climate (cold, desert, temperate). When a new day is reached via **next**, **prev** or **goto** commands (see below) a random weather is generated to that day. If you are not happy with the weather generated, you can use the command **weather** to generate another weather for that day. In future I will add a more deterministic way to setting weather. 6 | 7 | The calendar supports holydays. When a day is a holyday, the information about that holyday is showed. However I just entered holyday information for the Golarion Calendar. 8 | 9 | The calendar allows players and GM to add notes and logs about the current date. 10 | 11 | The calendar engine and the calendars information are separated. Each calendar is a “plugin” that can be founded at the plugin subdirectory. These plugins are fair simple, so I believe anyone with some programing skills can easily create a calendar plugin. 12 | 13 | ##Installing 14 | 15 | To install the Advanced Calendar Script you need to add two different js files to our **Roll20 Campaing**. The first one is the *AdvancedCalendar.js* which is located at the root directory. The other is one of the different plugins available in the plugin subdirectory. 16 | 17 | Currently there are 5 plugins calendars supported: 18 | 19 | - **ArtonPlugin.js**: compatible with Tormenta RPG, a Brazilian RPG setting. 20 | - **ForgottenPlugin.js**: compatible with Forgotten Realms setting. 21 | - **GolarionPlugin.js**: compatible the Pathfinder Official setting. 22 | - **GregorianPlugin.js**: our own Gregorian’s calendar. 23 | - **GregorianPluginPT.js**: our own Gregorian’s calendar, but in Portuguese. 24 | 25 | Chose one of the plugins and add it to your **Roll20 Campaing** 26 | 27 | ##Using 28 | 29 | The Advanced Calendar script currently supports the following commands: 30 | 31 | 32 | ``` 33 | !cal month 34 | ``` 35 | 36 | Shows a calendar for the current month. Can be used both by GMs and players. 37 | 38 | 39 | ``` 40 | !cal year 41 | ``` 42 | 43 | Shows a calendar for the current year. Can be used both by GMs and players. 44 | 45 | 46 | ``` 47 | !cal today 48 | ``` 49 | 50 | Shows the current day information in detail, including notes, logs and weather. Can be used both by GMs and players. 51 | 52 | ``` 53 | !cal next [] 54 | ``` 55 | 56 | Advances the current date by a number of days specified by the integer argument . If the argument DAYS is omitted, it advances 1 day. Can be used only by GMs. 57 | 58 | 59 | ``` 60 | !cal prev [] 61 | ``` 62 | 63 | Rewinds the current date by a number of days specified by the integer argument . If the argument DAYS is omitted, it rewinds 1 day. Can be used only by GMs. 64 | 65 | ``` 66 | !cal goto [ []] 67 | ``` 68 | 69 | Moves the current date to the date passed as argument. The arguments and are optional, if they are omitted, it uses the current date month and year instead. Can be used only by GMs. 70 | 71 | 72 | ``` 73 | !cal see [ []] 74 | ``` 75 | 76 | It is similar to **today**, but can be used to see detail information of any date passed as argument. The argument and are optional, if they are omitted, it uses the current date month and year instead. Can be used both by GMs and players. 77 | 78 | 79 | ``` 80 | !cal log -m 81 | ``` 82 | 83 | This command allows adding information to the current day. The value of the argument, as well as the **Roll20** user who used the command are stored. The stored values are displayed via **today** or **see** commands. 84 | 85 | ``` 86 | !cal note -m 87 | ``` 88 | 89 | This command is similar to the **log**, but can be used only by GM and the values stored are visible only to GMs. 90 | 91 | 92 | ``` 93 | !cal weather 94 | ``` 95 | 96 | This command can be used only by GM and it generates a random weather for the current day. Someone could call this command several times until it generates the desired weather for that date. 97 | -------------------------------------------------------------------------------- /calendar/plugins/GolarionPlugin.js: -------------------------------------------------------------------------------- 1 | var GolarionCalendar = GolarionCalendar || { 2 | name: "Golarion Calendar", 3 | 4 | startingDate : { 5 | day: 30, 6 | month: 6, 7 | year: 4715 8 | }, 9 | 10 | setting : { 11 | daysOfYear : 365, 12 | daysOfTheWeek: [ 13 | 'Sunday', 14 | 'Moonday', 15 | 'Toilday', 16 | 'Wealday', 17 | 'Oathday', 18 | 'Fireday', 19 | 'Starday' 20 | ], 21 | monthsOfTheYear: [ 22 | {name:'Abadius', days: 31, season: Season.WINTER}, 23 | {name:'Calistril', days: 28, season: Season.WINTER}, 24 | {name:'Pharast', days: 31, season: Season.SPRING}, 25 | {name:'Gozran', days: 30, season: Season.SPRING}, 26 | {name:'Desnus', days: 31, season: Season.SPRING}, 27 | {name:'Sarenith', days: 31, season: Season.SUMMER}, 28 | {name:'Erastus', days: 30, season: Season.SUMMER}, 29 | {name:'Arodus', days: 31, season: Season.SUMMER}, 30 | {name:'Rova', days: 30, season: Season.FALL}, 31 | {name:'Lamashan', days: 31, season: Season.FALL}, 32 | {name:'Neth', days: 30, season: Season.FALL}, 33 | {name:'Kuthona', days: 31, season: Season.WINTER} 34 | ], 35 | yearPrefix: 'AR', 36 | seasons : [ 37 | "Spring", 38 | "Summer", 39 | "Fall", 40 | "Winter" 41 | ], 42 | }, 43 | 44 | holidays : { 45 | "1/1" : [ 46 | { 47 | name: "Foundation Day", 48 | area: "Absalom", 49 | description: "A civil holiday celebrating the foundation of the city by the god Aroden" 50 | }, 51 | { 52 | name: "Pjallarane Day", 53 | area: "Irrisen", 54 | description: "This Irrisen New Year celebration commemorates the one-day rebellion launched by Queen Pjallarane against her mother, Baba Yaga." 55 | } 56 | ], 57 | "6/1" : [ 58 | { 59 | name: "Vault Day", 60 | area: "Abadar", 61 | description: "" 62 | } 63 | ], 64 | "20/1" : [ 65 | { 66 | name: "Ruby Prince's Birthday", 67 | area: "Osirion", 68 | description: "A national holiday in honor of the birthday of Khemet III, the Ruby Prince." 69 | } 70 | ], 71 | "2/2" : [ 72 | { 73 | name: "Merrymead ", 74 | area: "Druma, Cayden Cailean", 75 | description: "During this holiday in celebration of the approaching Spring, the previous year's alcohol is consumed." 76 | } 77 | ], 78 | "16/2" : [ 79 | { 80 | name: "King Eodred II's Birthday", 81 | area: "Korvosa", 82 | description: "A probably now defunct holiday honoring King Eodred (given the King's death in 4708 AR) on the occasion of his birthday." 83 | } 84 | ], 85 | "5/3" : [ 86 | { 87 | name: "Day of Bones", 88 | area: "Pharasma", 89 | description: "Priests and worshipers of the Lady of Graves parade the bodies of the recently dead on this holiday, holding free burials afterwards" 90 | } 91 | ], 92 | "6/3" : [ 93 | { 94 | name: "Sable Company Founding Day", 95 | area: "Korvosa", 96 | description: "A military holiday marked by parades, celebrating the founding of the Sable Company in 4409 AR" 97 | } 98 | ], 99 | "7/3" : [ 100 | { 101 | name: "Night of Tears", 102 | area: "Solku", 103 | description: "A solemn vigil commemorating those lost in the Battle of Red Hail in 4701 AR." 104 | } 105 | ], 106 | "13/3" : [ 107 | { 108 | name: "Kaliashahrim", 109 | area: "Qadira", 110 | description: "This national holiday celebrates the Padishah Emperor and Qadira's allegiance to him." 111 | } 112 | ], 113 | "20/3" : [ 114 | { 115 | name: "Vernal Equinox", 116 | area: "", 117 | description: "", 118 | }, 119 | { 120 | name: "Days of Wrath", 121 | area: "Cheliax, Asmodeus", 122 | description: "Contests and blood sports are held to honor and elevate those who are superior." 123 | }, 124 | { 125 | name: "Firstbloom", 126 | area: "Gozreh", 127 | description: "Fertility dances celebrate the coming of spring." 128 | }, 129 | { 130 | name: "Planting Week", 131 | area: "Erastil", 132 | description: "This holy week to the god Erastil is a time of heavy work in the fields for farmers." 133 | } 134 | ], 135 | "26/3" : [ 136 | { 137 | name: "Conquest Day", 138 | area: "Nex", 139 | description: "A national holiday in which citizens of Nex renew their pledge to conquer their eternal enemy, Geb." 140 | } 141 | ], 142 | "7/4" : [ 143 | { 144 | name: "Currentseve", 145 | area: "Gozreh", 146 | description: "On this religious holiday, all who travel on the water make offerings to Gozreh in the hopes of safe passage for the coming year." 147 | } 148 | ], 149 | "15/4" : [ 150 | { 151 | name: "Taxfest", 152 | area: "Abadar", 153 | description: "Priests of the god accompany tax collectors on this holiday. After completing their duties, the church sponsors large (and free) public celebrations to help mend relations with common folk." 154 | } 155 | ], 156 | "16/4" : [ 157 | { 158 | name: "Wrights of Augustana", 159 | area: "Andoran", 160 | description: "This local festival in the Andoran port city of Augustana is held to honor and celebrate the local shipbuilding industry as well as the navy." 161 | } 162 | ], 163 | "2/5" : [ 164 | { 165 | name: "Ascendance Night", 166 | area: "Norgorber", 167 | description: "Day marking the apotheosis of the Reaper of Reputation." 168 | } 169 | ], 170 | "13/5" : [ 171 | { 172 | name: "Old-Mage Day", 173 | area: "Nantambu, Mwangi Expanse", 174 | description: "Holiday celebrating Old-Mage Jatembe, the father of Garundi magic." 175 | } 176 | ], 177 | "31/5" : [ 178 | { 179 | name: "Angel Day", 180 | area: "Magnimar, Varisia", 181 | description: "A day of masquerades acting as a celebration to the Empyreal Lords" 182 | } 183 | ], 184 | "3/6" : [ 185 | { 186 | name: "Day of Destiny Festival", 187 | area: "Korvosa", 188 | description: "This day celebrates the day the emperor of Cheliax signed the charter for the founding of the city of Korvosa." 189 | }, 190 | { 191 | name: "Liberty Day", 192 | area: "Andoran", 193 | description: "Holiday celebrating Andoran's independence" 194 | } 195 | ], 196 | "10/6" : [ 197 | { 198 | name: "Burning Blades", 199 | area: "Serenrae", 200 | description: "The holy, month-long festival ends on this day, featuring dances with flaming blades." 201 | } 202 | ], 203 | "20/6" : [ 204 | { 205 | name: "Summer Solstice", 206 | area: "", 207 | description: "", 208 | }, 209 | { 210 | name: "Days of Wrath", 211 | area: "Cheliax, Asmodeus", 212 | description: "Contests and blood sports are held to honor and elevate those who are superior." 213 | }, 214 | { 215 | name: "Ritual of Stardust", 216 | area: "Desna", 217 | description: "Festival held in the evening and through the night, where Desna's faithful sing songs and throw sand and powdered gems into bonfires." 218 | }, 219 | { 220 | name: "Sunwrought Festival", 221 | area: "Sarenrae", 222 | description: "Day commemorating the defeat of Rovagug by Sarenrae, celebrated with the flying of kites, fireworks, and gift giving." 223 | } 224 | ], 225 | "21/6" : [ 226 | { 227 | name: "Talon Tag", 228 | area: "Andoran", 229 | description: "The Eagle Knights perform aerial displays in Almas on this day." 230 | } 231 | ], 232 | "22/6" : [ 233 | { 234 | name: "Riverwind Festival", 235 | area: "Korvosa", 236 | description: "An early summer holiday that honors a cooling shift in the winds, celebrated with much drinking." 237 | } 238 | ], 239 | "3/7" : [ 240 | { 241 | name: "Archer's Day or Archerfeast", 242 | area: "Erastil", 243 | description: "Holy day celebrated with archery contests, bartering for livestock, and the courting of women." 244 | } 245 | ], 246 | "14/7" : [ 247 | { 248 | name: "Founding Festival", 249 | area: "Korvosa", 250 | description: "An all-night party filled with fireworks and alcohol commemorating the founding of the city in 4407 AR." 251 | } 252 | ], 253 | "17/7" : [ 254 | { 255 | name: "Burning Night", 256 | area: "Razmiran", 257 | description: "Items or people who have transgressed against the god-king of Razmiran are burned on this day." 258 | } 259 | ], 260 | "15/7" : [ 261 | { 262 | name: "Kianidi Festival", 263 | area: "Garundi", 264 | description: "Celebration where tribal ties are honored and stories of travels are shared." 265 | } 266 | ], 267 | "1/8" : [ 268 | { 269 | name: "Inheritor's Ascendance", 270 | area: "Iomedae", 271 | description: "" 272 | } 273 | ], 274 | "6/8" : [ 275 | { 276 | name: "First Crusader Day", 277 | area: "Mendev", 278 | description: "Holiday in celebration of the continuing crusade against the demons of the Worldwound." 279 | } 280 | ], 281 | "9/8" : [ 282 | { 283 | name: "Day of Silenced Whispers", 284 | area: "Ustalav", 285 | description: "Holiday celebrating the defeat of the Whispering Tyrant and the freeing of Ustalav." 286 | } 287 | ], 288 | "10/8" : [ 289 | { 290 | name: "Founding Day", 291 | area: "Ilsurian, Varisia", 292 | description: "Festival celebrating the founding by Ilsur of the town of Ilsurian in 4631 AR." 293 | } 294 | ], 295 | "16/8" : [ 296 | { 297 | name: "Armasse", 298 | area: "Aroden, Iomedae", 299 | description: "Holy day where commoners are trained to fight and historical tales are told with the hope that someone will learn from them." 300 | } 301 | ], 302 | "31/8" : [ 303 | { 304 | name: "Saint Alika's Birthday", 305 | area: "Korvosa", 306 | description: "Quiet holiday honoring the birth of Saint Alika the Martyr." 307 | } 308 | ], 309 | "6/9" : [ 310 | { 311 | name: "Start of Classes", 312 | area: "Acadamae, Arcanamirium, College of Mysteries, Clockwork Cathedral", 313 | description: "" 314 | } 315 | ], 316 | "16/9" : [ 317 | { 318 | name: "Autumnal Carpentry Court", 319 | area: "Andoran", 320 | description: "" 321 | } 322 | ], 323 | "19/9" : [ 324 | { 325 | name: "Day of the Inheritor", 326 | area: "Iomedae", 327 | description: "Holiday commemorating the church of Iomedae's adoption of the forlorn faithful of the dead god Aroden into their midst." 328 | } 329 | ], 330 | "22/9" : [ 331 | { 332 | name: "Autumnal Equinox", 333 | area: "", 334 | description: "", 335 | }, 336 | { 337 | name: "Days of Wrath", 338 | area: "Cheliax, Asmodeus", 339 | description: "Contests and blood sports are held to honor and elevate those who are superior." 340 | }, 341 | { 342 | name: "Swallowtail Festival", 343 | area: "Desna, Sandpoint", 344 | description: "Holiday celebrated with storytelling, feasting, and the release of butterflies." 345 | } 346 | ], 347 | "26/9" : [ 348 | { 349 | name: "Feast of Szurpade", 350 | area: "Irrisen", 351 | description: "This \"celebration of plenty\" festival mocks the traditional harvest festivals celebrated in the region before Baba Yaga and her eternal winter descended upon the land." 352 | } 353 | ], 354 | "29/9" : [ 355 | { 356 | name: "Day of Sundering", 357 | area: "Ydersius", 358 | description: "Once many holidays were celebrated by the faith of Ydersius, but today only this date has much significance." 359 | } 360 | ], 361 | "6/10" : [ 362 | { 363 | name: "Ascendance Day", 364 | area: "Iomedae", 365 | description: "Holiday marking the ascension of the goddess Iomedae after taking the Test of the Starstone." 366 | } 367 | ], 368 | "19/10" : [ 369 | { 370 | name: "Bastion Day", 371 | area: "Solku", 372 | description: "A festival honoring the founding of the town of Solku." 373 | } 374 | ], 375 | "27/10" : [ 376 | { 377 | name: "Jestercap", 378 | area: "Andoran, Druma, Taldor", 379 | description: "Holiday marked by the playing of many practical jokes; particularly popular among gnomes." 380 | } 381 | ], 382 | "30/10" : [ 383 | { 384 | name: "Allbirth", 385 | area: "Lamashu", 386 | description: "" 387 | }, 388 | { 389 | name: "Festival of the Witch", 390 | area: "Irrisen", 391 | description: "Festival celebrating witchcraft and the central part it plays in Irriseni culture." 392 | } 393 | ], 394 | "5/11" : [ 395 | { 396 | name: "Independence Day", 397 | area: "Galt", 398 | description: "Marks the beginning of the Red Revolution." 399 | } 400 | ], 401 | "8/11" : [ 402 | { 403 | name: "Abjurant Day", 404 | area: "Nethys", 405 | description: "Day of communal strengthening of defenses and the teaching of magic to children." 406 | } 407 | ], 408 | "13/11" : [ 409 | { 410 | name: "Great Fire Remembrance", 411 | area: "Korvosa", 412 | description: "Holiday commemorates the dead of the Great Fire of 4429 AR." 413 | } 414 | ], 415 | "14/11" : [ 416 | { 417 | name: "Even-Tongued Day", 418 | area: "Cheliax, Asmodeus", 419 | description: "Day that celebrates when Andoran, Galt, and Isger were put under Chelish control." 420 | } 421 | ], 422 | "18/11" : [ 423 | { 424 | name: "Evoking Day", 425 | area: "Nethys", 426 | description: "Holiday marked by displays of fireworks and magical duels (both mock and real)." 427 | } 428 | ], 429 | "23/11" : [ 430 | { 431 | name: "Seven Veils", 432 | area: "", 433 | description: "Celebration of the unity of all races, commemorated by multi-species masquerade balls." 434 | } 435 | ], 436 | "24/11" : [ 437 | { 438 | name: "Baptism of Ice", 439 | area: "Irrisen", 440 | description: "A fertility festival where the children born in the previous year are paraded through the towns." 441 | } 442 | ], 443 | "28/11" : [ 444 | { 445 | name: "Transmutatum", 446 | area: "Nethys", 447 | description: "Festival promoting self-improvement." 448 | } 449 | ], 450 | "7/12" : [ 451 | { 452 | name: "Pseudodragon Festival", 453 | area: "Korvosa", 454 | description: "Holiday marking the return of the wild pseudodragons to Conqueror's Bay." 455 | } 456 | ], 457 | "11/12" : [ 458 | { 459 | name: "Ascension Day", 460 | area: "Cayden Cailean", 461 | description: "Holiday celebrating Cayden's divine ascension after taking the Test of the Starstone." 462 | } 463 | ], 464 | "21/12" : [ 465 | { 466 | name: "Winter Solstice", 467 | area: "", 468 | description: "", 469 | }, 470 | { 471 | name: "Days of Wrath", 472 | area: "Cheliax, Asmodeus", 473 | description: "Contests and blood sports are held to honor and elevate those who are superior." 474 | }, 475 | { 476 | name: "Crystalhue", 477 | area: "Shelyn", 478 | description: "Holiday marked by the creation of artistic works, and the start of romantic courtships." 479 | }, 480 | { 481 | name: "Ritual of Stardust", 482 | area: "Desna", 483 | description: "Bi-annual festival held on the solstices, where the faithful of Desna sing songs through the night around bonfires." 484 | } 485 | ], 486 | "31/12" : [ 487 | { 488 | name: "Night of the Pale", 489 | area: "", 490 | description: "Night of morbid revelry, as people wait indoors for the ghosts of last year's dead to pass by their homes." 491 | }, 492 | { 493 | name: "The Final Day", 494 | area: "Groetus", 495 | description: "Cultists of Groetus perform an hour's silence at dusk on the last day of the year seeking guidance from their god about the End Time." 496 | } 497 | ] 498 | } 499 | 500 | }; 501 | 502 | CALENDAR = GolarionCalendar; -------------------------------------------------------------------------------- /calendar/AdvancedCalendar.js: -------------------------------------------------------------------------------- 1 | var Climate = { 2 | COLD : 0, 3 | DESERT : 1, 4 | TEMPERATE : 2, 5 | }; 6 | 7 | var Season = { 8 | SPRING : 0, 9 | SUMMER : 1, 10 | FALL : 2, 11 | WINTER : 3 12 | }; 13 | 14 | var WeatherRandomGenerator = WeatherRandomGenerator || { 15 | version : 1.0, 16 | 17 | CheckInstall: function(calendar) { 18 | if(! state.hasOwnProperty('WeatherRandomGenerator') || state.WeatherRandomGenerator.version != WeatherRandomGenerator.version) 19 | { 20 | // Default Settings stored in the state. 21 | state.WeatherRandomGenerator = { 22 | version: AdvancedCalendar.version, 23 | climate: Climate.TEMPERATE, 24 | options: { 25 | useMetricalSystem : true 26 | } 27 | } 28 | } 29 | }, 30 | 31 | _randomNumber : function(min, max) { 32 | return Math.floor((Math.random() * (max-min)) + min); 33 | }, 34 | 35 | _GetWeatherAsString : function(w) { 36 | return w.name + ' - ' + w.description; 37 | }, 38 | 39 | _normalWeather : function(climate, season) { 40 | if (climate == Climate.COLD) { 41 | var w = { 42 | name : "cold", 43 | temperatureHigh : WeatherRandomGenerator._randomNumber(0, 40), 44 | windSpeed : WeatherRandomGenerator._randomNumber(0, 10), 45 | description: "", 46 | }; 47 | 48 | w["temperatureLow"] = w.temperatureHigh - WeatherRandomGenerator._randomNumber(10, 20); 49 | 50 | return w; 51 | 52 | } else if (climate == Climate.TEMPERATE) { 53 | if (season == Season.SPRING || season == Season.FALL) { 54 | var w = { 55 | temperatureHigh : WeatherRandomGenerator._randomNumber(40, 70), 56 | windSpeed : WeatherRandomGenerator._randomNumber(0, 10), 57 | description: "", 58 | }; 59 | 60 | w["temperatureLow"] = w.temperatureHigh - WeatherRandomGenerator._randomNumber(10, 20); 61 | 62 | if (w.temperatureHigh < 50) { 63 | w["name"] = "cold"; 64 | } else { 65 | w["name"] = "warm"; 66 | } 67 | 68 | 69 | return w; 70 | } else if (season == Season.SUMMER) { 71 | var w = { 72 | name : "warm", 73 | temperatureHigh : WeatherRandomGenerator._randomNumber(60, 85), 74 | windSpeed : WeatherRandomGenerator._randomNumber(0, 10), 75 | description: "", 76 | }; 77 | 78 | w["temperatureLow"] = w.temperatureHigh - WeatherRandomGenerator._randomNumber(10, 20); 79 | 80 | return w; 81 | 82 | } else { 83 | var w = { 84 | name : "cold", 85 | temperatureHigh : WeatherRandomGenerator._randomNumber(0, 40), 86 | windSpeed : WeatherRandomGenerator._randomNumber(0, 10), 87 | description: "", 88 | }; 89 | 90 | w["temperatureLow"] = w.temperatureHigh - WeatherRandomGenerator._randomNumber(10, 20); 91 | 92 | return w; 93 | } 94 | 95 | } else { 96 | var w = { 97 | name : "hot", 98 | temperatureHigh : WeatherRandomGenerator._randomNumber(85, 110), 99 | windSpeed : WeatherRandomGenerator._randomNumber(0, 10), 100 | description: "", 101 | }; 102 | 103 | w["temperatureLow"] = w.temperatureHigh - WeatherRandomGenerator._randomNumber(10, 20); 104 | 105 | return w; 106 | } 107 | }, 108 | 109 | _abnormalWeather : function(climate, season) { 110 | var w = WeatherRandomGenerator._normalWeather(climate, season); 111 | 112 | if (climate == Climate.DESERT) { 113 | w.name = "windy", 114 | w.windSpeed = WeatherRandomGenerator._randomNumber(10, 30); 115 | } else { 116 | var d100 = WeatherRandomGenerator._randomNumber(1, 100); 117 | if ( (climate == Climate.COLD && d100 <= 30) || (climate == Climate.TEMPERATE && d100 <= 50) ) { 118 | w.name = "heatWave"; 119 | w.temperatureHigh += 10; 120 | w.temperatureLow += 10; 121 | } else { 122 | w.name = "coldSnap"; 123 | w.temperatureHigh -= 10; 124 | w.temperatureLow -= 10; 125 | } 126 | } 127 | return w; 128 | }, 129 | 130 | _inclementWeather : function(climate, season) { 131 | var w = WeatherRandomGenerator._normalWeather(climate, season); 132 | 133 | if (climate == Climate.DESERT) { 134 | w.name = "windy"; 135 | w.windSpeed = WeatherRandomGenerator._randomNumber(10, 30); 136 | } else if (climate == Climate.COLD) { 137 | w.name = 'snow'; 138 | w.description = "" + WeatherRandomGenerator._randomNumber(2, 8) + " hours of snow."; 139 | } else { 140 | var d100 = WeatherRandomGenerator._randomNumber(1, 100); 141 | if (d100 <= 30) { 142 | w.name = "fog"; 143 | w.description = "" + WeatherRandomGenerator._randomNumber(2, 8) + " hours of fog."; 144 | } else if (d100 <=90) { 145 | if (w.temperatureHigh <= 30) { 146 | w.name = "snow"; 147 | w.description = "" + WeatherRandomGenerator._randomNumber(2, 8) + " hours of snow."; 148 | } else { 149 | w.name = "rain"; 150 | w.description = "" + WeatherRandomGenerator._randomNumber(2, 8) + " hours of rain."; 151 | } 152 | } else { 153 | if (w.temperatureHigh <= 30) { 154 | w.name = "sleet"; 155 | w.description = "" + WeatherRandomGenerator._randomNumber(2, 8) + " hours of sleet."; 156 | } else { 157 | w.name = "hail"; 158 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 20) + " of minutes of hail followed by " 159 | + WeatherRandomGenerator._randomNumber(1,4) + " hours of rain."; 160 | } 161 | } 162 | } 163 | 164 | return w; 165 | }, 166 | 167 | _storm : function(climate, season) { 168 | var w = WeatherRandomGenerator._normalWeather(climate, season); 169 | w.windSpeed = WeatherRandomGenerator._randomNumber(30, 50); 170 | 171 | if (climate == Climate.DESERT) { 172 | w.name = "duststorm"; 173 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 7) + " hours of duststom."; 174 | } else if (climate == Climate.COLD) { 175 | w.name = "snowstorm"; 176 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 7) + " hours of snowstorm."; 177 | } else { 178 | if (w.temperatureHigh <= 30) { 179 | w.name = "snowstorm"; 180 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 7) + " hours of snowstorm."; 181 | } else { 182 | var d10 = WeatherRandomGenerator._randomNumber(1, 20); 183 | 184 | if (d10 == 1) { 185 | w.name = "tornado"; 186 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 7) + " hours of thunderstorm with " 187 | + "tornado (wind speed " + WeatherRandomGenerator.GetSpeed(WeatherRandomGenerator._randomNumber(175, 300)) 188 | + ") for " + WeatherRandomGenerator._randomNumber(10, 60) + " minutes."; 189 | } else { 190 | w.name = "thunderstorm"; 191 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 7) + " hours of thunderstorm."; 192 | 193 | } 194 | } 195 | } 196 | 197 | return w; 198 | }, 199 | 200 | _powerfulStorm : function(climate, season) { 201 | 202 | var w = WeatherRandomGenerator._normalWeather(climate, season); 203 | 204 | if (climate == Climate.DESERT) { 205 | w.name = "downspor"; 206 | w.windSpeed = WeatherRandomGenerator._randomNumber(30, 50); 207 | w.description = "" + WeatherRandomGenerator._randomNumber(2, 8) + " hours of downspor."; 208 | } else if (climate == Climate.COLD) { 209 | w.name = "blizzard"; 210 | w.windSpeed = WeatherRandomGenerator._randomNumber(51, 74); 211 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 3) + " days of blizzard."; 212 | } else { 213 | if (w.temperatureHigh <= 30) { 214 | w.name = "blizzard"; 215 | w.windSpeed = WeatherRandomGenerator._randomNumber(51, 74); 216 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 3) + " days of blizzard."; 217 | } else { 218 | var percent = WeatherRandomGenerator._randomNumber(1, 100); 219 | if (w.windSpeed < 75) { 220 | w.name = "windstorm"; 221 | w.windSpeed = WeatherRandomGenerator._randomNumber(51, 74); 222 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 6) + " hours of windstorm."; 223 | } else { 224 | w.name = "hurricane"; 225 | w.windSpeed = WeatherRandomGenerator._randomNumber(75, 174); 226 | w.description = "" + WeatherRandomGenerator._randomNumber(1, 7) + " days of hurricane."; 227 | } 228 | } 229 | } 230 | 231 | return w; 232 | 233 | }, 234 | 235 | GetSpeed : function (speed) { 236 | if (state.WeatherRandomGenerator.options.useMetricalSystem) { 237 | return Math.ceil(speed*1.6) + " km/h"; 238 | } else { 239 | return speed + " mph"; 240 | } 241 | }, 242 | 243 | GetTemperature : function (temperature) { 244 | if (state.WeatherRandomGenerator.options.useMetricalSystem) { 245 | return Math.ceil((temperature-32)/1.8) + " °C"; 246 | } else { 247 | return temperature + " °F"; 248 | } 249 | }, 250 | 251 | GenerateWeather : function(season) { 252 | var d100 = WeatherRandomGenerator._randomNumber(1, 100); 253 | var climate = state.WeatherRandomGenerator.climate; 254 | 255 | if (d100 <= 70) { 256 | //Normal Weather 257 | return WeatherRandomGenerator._normalWeather(climate, season); 258 | } else if (d100 <= 80) { 259 | //Abnormal Weather 260 | return WeatherRandomGenerator._abnormalWeather(climate, season); 261 | } else if (d100 <= 90) { 262 | //Inclement weather 263 | return WeatherRandomGenerator._inclementWeather(climate, season); 264 | } else if (d100 <= 99) { 265 | //Storm 266 | return WeatherRandomGenerator._storm(climate, season); 267 | } else { 268 | //Powerful Storm 269 | return WeatherRandomGenerator._powerfulStorm(climate, season); 270 | } 271 | 272 | }, 273 | 274 | RegisterEventHandlers : function() { 275 | 276 | } 277 | }; 278 | 279 | var AdvancedCalendar = AdvancedCalendar || { 280 | version: 1.0, 281 | lunarPhaseSize: 15, 282 | lunarPhasesImage: 'https://s3.amazonaws.com/files.d20.io/images/4277527/CJJWBbiHx3jHglPdccPx3A/max.png?1401939451', 283 | clearImage: 'https://s3.amazonaws.com/files.d20.io/images/4277467/iQYjFOsYC5JsuOPUCI9RGA/max.png?1401938659', 284 | seasonImage: 'https://s3.amazonaws.com/files.d20.io/images/15888656/ICJhf5iNk0wtC3GbK0Y6iQ/original.png?14544194835', 285 | seasonImageSize : 30, 286 | 287 | weatherImage : 'https://s3.amazonaws.com/files.d20.io/images/15894686/Zaby88WaMRSRO104_1eAug/original.png?14544320015', 288 | weatherImageSize : 30, 289 | weatherImageBorderHorizontal: 14.5, 290 | weatherImageVerticalBorder: 11, 291 | 292 | weathersIcons : { 293 | hot: {name: "hot", x: 0, y: 1}, 294 | warm: {name: "warm", x: 1, y: 1}, 295 | cold: {name: "cold", x: 2, y: 1}, 296 | 297 | heatWave: {name: "heat wave", x: 3, y: 7}, 298 | coldSnap: {name: "cold snap", x: 4, y: 7}, 299 | 300 | calm: {name: "calm", x: 0, y: 5}, 301 | windy: {name: "windy", x: 1, y: 5}, 302 | windstorm: {name: "windstorm", x: 2, y: 5}, 303 | hurricane: {name: "hurricane", x: 3, y: 5}, 304 | tornado: {name: "tornado", x: 3, y: 5}, 305 | 306 | fog: {name: "fog", x: 7, y: 4}, 307 | rain: {name: "rain", x: 6, y: 1}, 308 | hail: {name: "hail", x: 9, y: 1}, 309 | downspor: {name: "downspor", x: 0, y: 2}, 310 | thunderstorm: {name: "thunderstorm", x: 3, y: 2}, 311 | 312 | snow: {name: "snow", x: 5, y: 4}, 313 | sleet: {name: "sleet", x: 3, y: 3}, 314 | snowstorm: {name: "snowstorm", x: 8, y: 4}, 315 | blizzard: {name: "blizzard", x: 9, y: 4}, 316 | 317 | duststorm: {name: "duststorm", x: 1, y: 4} 318 | }, 319 | 320 | _Ordinal: function(num) { 321 | var ones=(num%10); 322 | var tens=((num%100)-ones); 323 | switch(ones) 324 | { 325 | case 1: return ((10 == tens) ? 'th' : 'st'); 326 | case 2: return ((10 == tens) ? 'th' : 'nd'); 327 | case 3: return ((10 == tens) ? 'th' : 'rd'); 328 | default: return 'th'; 329 | } 330 | }, 331 | 332 | _GetOptionsFromTokens: function (tokens) { 333 | var options={}; 334 | var switches=_.filter(tokens, function(tok){ 335 | return null != tok.match(/^--/); 336 | }); 337 | _.each(switches,function(s){ 338 | switch(s) 339 | { 340 | case '--lunar': options.showLunarPhases=true; break; 341 | case '--nolunar': options.showLunarPhases=false; break; 342 | 343 | } 344 | }); 345 | 346 | return options; 347 | }, 348 | 349 | CheckInstall: function(calendar) { 350 | if(! state.hasOwnProperty('AdvancedCalendar') || state.AdvancedCalendar.version != AdvancedCalendar.version) 351 | { 352 | log("Reseting calendar data..."); 353 | // Default Settings stored in the state. 354 | state.AdvancedCalendar = { 355 | version: AdvancedCalendar.version, 356 | now: calendar.startingDate, 357 | setting: calendar.setting, 358 | holidays : calendar.holidays, 359 | schedule: {}, 360 | } 361 | } 362 | }, 363 | 364 | SetWeather: function(date, weather) { 365 | var dateId = (AdvancedCalendar._GetTotalDaysForDate(date)).toString(); 366 | 367 | if (! _.has(state.AdvancedCalendar.schedule, dateId)) { 368 | state.AdvancedCalendar.schedule[dateId] = {}; 369 | } 370 | 371 | state.AdvancedCalendar.schedule[dateId]["weather"] = weather; 372 | }, 373 | 374 | GetWeather: function(date) { 375 | var dateId = (AdvancedCalendar._GetTotalDaysForDate(date)).toString(); 376 | var sched = state.AdvancedCalendar.schedule; 377 | 378 | if (_.has(sched, dateId) && _.has(sched[dateId], "weather")) { 379 | return sched[dateId]["weather"]; 380 | } else { 381 | var s = state.AdvancedCalendar.setting; 382 | var season = s.monthsOfTheYear[date.month -1].season; 383 | log(season); 384 | var w = WeatherRandomGenerator.GenerateWeather(season); 385 | sched[dateId] = {}; 386 | sched[dateId]["weather"] = w; 387 | return w; 388 | } 389 | }, 390 | 391 | ChangeWeather: function(date) { 392 | var dateId = (AdvancedCalendar._GetTotalDaysForDate(date)).toString(); 393 | var sched = state.AdvancedCalendar.schedule; 394 | 395 | if (_.has(sched, dateId) && _.has(sched[dateId], "weather")) { 396 | sched[dateId]["weather"] = WeatherRandomGenerator.GenerateWeather(season); 397 | } else { 398 | var s = state.AdvancedCalendar.setting; 399 | var season = s.monthsOfTheYear[date.month -1].season; 400 | log(season); 401 | var w = WeatherRandomGenerator.GenerateWeather(season); 402 | sched[dateId] = {}; 403 | sched[dateId]["weather"] = w; 404 | } 405 | }, 406 | 407 | AddLog: function(date, character, log, isNote) { 408 | var dateId = (AdvancedCalendar._GetTotalDaysForDate(date)).toString(); 409 | 410 | if (! _.has(state.AdvancedCalendar.schedule, dateId)) { 411 | state.AdvancedCalendar.schedule[dateId] = {}; 412 | state.AdvancedCalendar.schedule[dateId]["logs"] = []; 413 | } else if (! _.has(state.AdvancedCalendar.schedule[dateId], "logs")) { 414 | state.AdvancedCalendar.schedule[dateId]["logs"] = []; 415 | } 416 | 417 | state.AdvancedCalendar.schedule[dateId]["logs"].push( 418 | {character: character, log: log, isNote: isNote }); 419 | }, 420 | 421 | GetLogs: function(date) { 422 | var dateId = (AdvancedCalendar._GetTotalDaysForDate(date)).toString(); 423 | var sched = state.AdvancedCalendar.schedule; 424 | 425 | if (_.has(sched, dateId) && _.has(sched[dateId], "logs")) { 426 | return sched[dateId]["logs"]; 427 | } else { 428 | return []; 429 | } 430 | }, 431 | 432 | AdvanceDays: function(days){ 433 | var n = state.AdvancedCalendar.now; 434 | var s = state.AdvancedCalendar.setting; 435 | 436 | log(n); 437 | 438 | var y = Math.floor(days/s.daysOfYear); 439 | days-=(y*s.daysOfYear); 440 | 441 | log(n.day + days); 442 | log(s.monthsOfTheYear[n.month -1].days); 443 | 444 | while (n.day + days > s.monthsOfTheYear[n.month -1].days) { 445 | days = days - s.monthsOfTheYear[n.month -1].days + n.day; 446 | 447 | n.day = 0; 448 | n.month++; 449 | 450 | if (n.month > 12) { 451 | n.month = 1; 452 | n.year++; 453 | } 454 | 455 | } 456 | 457 | log("days: " + days); 458 | log("years: " + y); 459 | 460 | n.day += days; 461 | n.year = n.year + y; 462 | 463 | state.AdvancedCalendar.now=n; 464 | }, 465 | 466 | RemoveDays: function(days){ 467 | var n = state.AdvancedCalendar.now; 468 | var s = state.AdvancedCalendar.setting; 469 | 470 | var y = Math.floor(days/s.daysOfYear); 471 | days-=(y*s.daysOfYear); 472 | 473 | while (days >= n.day) { 474 | days = days - n.day; 475 | 476 | n.month--; 477 | if (n.month < 1) { 478 | n.month = 12; 479 | n.year--; 480 | } 481 | 482 | n.day = s.monthsOfTheYear[n.month -1].days; 483 | 484 | } 485 | 486 | n.day-=days; 487 | n.year-=y; 488 | 489 | state.AdvancedCalendar.now=n; 490 | }, 491 | 492 | _GetTotalDaysForDate: function(d) { 493 | var s = state.AdvancedCalendar.setting; 494 | var totalDays = d.year*s.daysOfYear; 495 | 496 | 497 | for (var i=0; i < d.month-1; i++) { 498 | totalDays += s.monthsOfTheYear[i].days; 499 | } 500 | totalDays += d.day; 501 | return totalDays; 502 | }, 503 | 504 | _GetWeekDayForDate: function(d) { 505 | var s = state.AdvancedCalendar.setting; 506 | var totalDays = AdvancedCalendar._GetTotalDaysForDate(d); 507 | var weekday = totalDays % s.daysOfTheWeek.length; 508 | return ( weekday ? weekday : 7); 509 | }, 510 | 511 | _GetPhaseForDate: function(d,options){ 512 | var opt=_.defaults((options||{}),{ 513 | showLunarPhases: true 514 | }); 515 | return ((opt.showLunarPhases)?( 516 | '' 522 | ):('')); 523 | }, 524 | 525 | _GetWeatherForDate: function(d, options) { 526 | var opt=_.defaults((options||{}),{ 527 | showWeather: true 528 | }); 529 | 530 | var w = AdvancedCalendar.GetWeather(d); 531 | 532 | 533 | return ((opt.showWeather)?( 534 | '
' 536 | + AdvancedCalendar._GetWeatherImage(w) 537 | + " " + AdvancedCalendar.weathersIcons[w.name].name 538 | +'
' 539 | +'
' 540 | + "max: " + WeatherRandomGenerator.GetTemperature(w.temperatureHigh) 541 | + " min: " + WeatherRandomGenerator.GetTemperature(w.temperatureLow) + '
' 542 | + "wind speed: " + WeatherRandomGenerator.GetSpeed(w.windSpeed) 543 | + "
" + w.description 544 | +'
' 545 | ):('')); 546 | 547 | }, 548 | 549 | 550 | _GetWeatherImage: function(w) { 551 | var i = AdvancedCalendar.weathersIcons[w.name]; 552 | 553 | return ''; 559 | }, 560 | 561 | _GetSeasonForDate: function(d,options){ 562 | s = state.AdvancedCalendar.setting; 563 | var opt=_.defaults((options||{}),{ 564 | showSeason: true 565 | }); 566 | 567 | return ((opt.showSeason)?( 568 | '
' 570 | +AdvancedCalendar._GetSeasonImageForDate(d) 571 | + " " + s.seasons[s.monthsOfTheYear[d.month-1].season] 572 | +'
' 573 | ):('')); 574 | }, 575 | 576 | _GetSeasonImageForDate: function(d,options){ 577 | s = state.AdvancedCalendar.setting; 578 | var opt=_.defaults((options||{}),{ 579 | showSeason: true 580 | }); 581 | 582 | return ((opt.showSeason)?( 583 | '' 588 | ):('')); 589 | }, 590 | 591 | _GetHolidays: function(d, options) { 592 | var opt=_.defaults( (options||{}), { 593 | showHolidays: true 594 | }); 595 | 596 | var s = state.AdvancedCalendar.holidays; 597 | var dayId = d.day + "/" + d.month; 598 | 599 | if (opt.showHolidays && _.has(s, dayId)) { 600 | var temp = "
"; 601 | 602 | _.each(s[dayId], function(h) { 603 | var t = '' + h.name; 604 | 605 | if (h.area != "") { 606 | t += " (" + h.area + "): " 607 | } else { 608 | t += ": :" 609 | } 610 | 611 | if (h.description != "") { 612 | t += h.description; 613 | } 614 | temp += "
" + t; 615 | }); 616 | temp += "
"; 617 | 618 | return temp; 619 | } else { 620 | return ''; 621 | } 622 | }, 623 | 624 | _GetLogs: function(d, options) { 625 | var opt=_.defaults( (options||{}), { 626 | showLogs: true 627 | }); 628 | 629 | var logs = AdvancedCalendar.GetLogs(d); 630 | 631 | if (opt.showLogs) { 632 | var temp = "
"; 633 | 634 | _.each(logs, function(h) { 635 | if (!h.isNote) { 636 | var t = '' + h.character + ": "; 637 | 638 | if (h.description != "") { 639 | t += h.log; 640 | } 641 | temp += "
" + t; 642 | } 643 | }); 644 | temp += "
"; 645 | 646 | return temp; 647 | } else { 648 | return ''; 649 | } 650 | }, 651 | 652 | _GetDayForDate: function(d,options){ 653 | var opt=_.defaults((options||{}),{ 654 | }); 655 | 656 | var n = state.AdvancedCalendar.now; 657 | var img = AdvancedCalendar._GetPhaseForDate(d,opt) 658 | 659 | if(d.year == n.year && d.month == n.month && d.day == n.day) { 660 | return '
' 661 | +''+d.day 662 | +'' 663 | +img 664 | +'
'; 665 | } 666 | else if( (d.year < n.year) 667 | || ( (d.year <= n.year) && (d.month' 670 | +'' 671 | +'' 672 | +d.day 673 | +'' 674 | +img 675 | +'' 676 | +''; 677 | } 678 | else { 679 | return '
' 680 | +'' 681 | +d.day 682 | +'' 683 | +img 684 | +'
'; 685 | } 686 | }, 687 | 688 | _GetMonthForDate: function(d,options){ 689 | var opt=_.defaults((options||{}),{ 690 | showYear: true, 691 | showMonthNumber: false 692 | }); 693 | 694 | log(d); 695 | 696 | var s=state.AdvancedCalendar.setting; 697 | var daysHeader=''; 698 | _.each(s.daysOfTheWeek,function(d){ 699 | daysHeader+='
'+d.substring(0,2)+'
'; 700 | }); 701 | 702 | var mday=_.clone(d); 703 | var weeks=''; 704 | mday.day=1; 705 | 706 | var rows = 0; 707 | 708 | //First week can be trick 709 | var firstWeekDay = AdvancedCalendar._GetWeekDayForDate(mday); 710 | 711 | //placing empty spaces for month that doesn't start at first week day. 712 | weeks+='' 713 | for (i=1; i < firstWeekDay; i++) { 714 | weeks+=''; 715 | weeks+= '
' 716 | +'' 717 | +'' 718 | + ' ' 719 | +'' 720 | +'' 721 | +'
'; 722 | weeks+=''; 723 | } 724 | 725 | //Filling the rest of the week 726 | for (i = firstWeekDay; i <= s.daysOfTheWeek.length; i++) { 727 | weeks+=''; 728 | weeks+=AdvancedCalendar._GetDayForDate(mday,opt); 729 | weeks+=''; 730 | mday.day++; 731 | } 732 | weeks+=''; 733 | var restingWeeks = Math.ceil( (s.monthsOfTheYear[mday.month -1].days -1) / s.daysOfTheWeek.length); 734 | 735 | var j = 0; 736 | for (i= 0; i < restingWeeks; i++) { 737 | weeks+=''; 738 | for (j=0; j < s.daysOfTheWeek.length && mday.day <= s.monthsOfTheYear[mday.month -1].days; j++) { 739 | weeks+=''; 740 | weeks+=AdvancedCalendar._GetDayForDate(mday,opt); 741 | weeks+=''; 742 | mday.day++; 743 | } 744 | 745 | 746 | //filling the last week 747 | if (j < s.daysOfTheWeek.length) { 748 | for (var k = j; k < s.daysOfTheWeek.length; k++) { 749 | weeks+=''; 750 | weeks+= '
' 751 | +'' 752 | +'' 753 | + ' ' 754 | +'' 755 | +'' 756 | +'
'; 757 | weeks+=''; 758 | } 759 | } 760 | weeks+=''; 761 | } 762 | 763 | return '' 764 | +'' 771 | +''+daysHeader+'' 772 | +weeks 773 | +'
' 765 | +'
'+AdvancedCalendar._GetSeasonImageForDate(mday)+'
' 766 | +((opt.showMonthNumber)?('
'+d.month+'
'):('')) 767 | +'
' + s.monthsOfTheYear[d.month-1]["name"] 768 | +((opt.showYear)?(' '+d.year):('')) 769 | +'
' 770 | +'
'; 774 | }, 775 | 776 | _GetYearForDate: function(d,options){ 777 | var opt=_.defaults((options||{}),{ 778 | showLunarPhases: false, 779 | showYear: false, 780 | showMonthNumber: true 781 | }); 782 | var s=state.AdvancedCalendar.setting; 783 | var yday=_.clone(d); 784 | yday.day=1; 785 | yday.month=1; 786 | var months=''; 787 | _.each(s.monthsOfTheYear, function(r){ 788 | months+='
'; 789 | months+=AdvancedCalendar._GetMonthForDate(yday,opt); 790 | months+='
'; 791 | yday.month++; 792 | }); 793 | 794 | return '
' 795 | +'
'+d.year+' '+s.yearPrefix+'
'+months +'
'; 796 | }, 797 | 798 | _GetDateAsString: function(date){ 799 | var s=state.AdvancedCalendar.setting; 800 | return s.daysOfTheWeek[AdvancedCalendar._GetWeekDayForDate(date)-1] + " - " +date.day + " " 801 | + s.monthsOfTheYear[date.month-1]["name"] + ', ' + date.year + ' ' +s.yearPrefix; 802 | }, 803 | 804 | ShowDate: function(d,options) { 805 | var opt=_.defaults((options||{}),{ 806 | showLunarPhases: true 807 | }); 808 | sendChat('','/direct ' 809 | +'
' 812 | +AdvancedCalendar._GetDateAsString(d,opt) 813 | +' ' 814 | +AdvancedCalendar._GetPhaseForDate(d,opt) 815 | +AdvancedCalendar._GetSeasonForDate(d, opt) 816 | +AdvancedCalendar._GetWeatherForDate(d, opt) 817 | +AdvancedCalendar._GetHolidays(d, opt) 818 | +AdvancedCalendar._GetLogs(d, opt) 819 | +'
' 820 | ); 821 | }, 822 | 823 | 824 | ShowDateAsWhisper: function(who, d, options) { 825 | sendChat('','/w GM ' 826 | +AdvancedCalendar._GetDateAsString(d,options) 827 | +' ' 828 | +WeatherRandomGenerator._GetWeatherAsString(AdvancedCalendar.GetWeather(d), options) 829 | ); 830 | }, 831 | 832 | ShowFutureDate: function(d, options) { 833 | var opt=_.defaults((options||{}),{ 834 | showLunarPhases: true 835 | }); 836 | sendChat('','/direct ' 837 | +'
' 840 | +AdvancedCalendar._GetDateAsString(d,opt) 841 | +' ' 842 | +AdvancedCalendar._GetPhaseForDate(d,opt) 843 | +AdvancedCalendar._GetSeasonForDate(d, opt) 844 | +AdvancedCalendar._GetHolidays(d, opt) 845 | +AdvancedCalendar._GetLogs(d, opt) 846 | +'
' 847 | ); 848 | }, 849 | 850 | ShowMonth: function(d,options){ 851 | var opt=_.defaults((options||{}),{ 852 | }); 853 | sendChat('','/direct '+AdvancedCalendar._GetMonthForDate(d,opt)); 854 | }, 855 | 856 | ShowYear: function(d,options){ 857 | var opt=_.defaults((options||{}),{ 858 | showYear: false 859 | }); 860 | sendChat('','/direct '+AdvancedCalendar._GetYearForDate(d,opt)); 861 | }, 862 | 863 | GoToDate: function(day, month, year) { 864 | var s = state.AdvancedCalendar.setting; 865 | 866 | if (month < 1 || month > 12) { 867 | return; 868 | } 869 | 870 | if (day < 1 || day > s.monthsOfTheYear[state.AdvancedCalendar.now.month-1]) { 871 | return; 872 | } 873 | 874 | state.AdvancedCalendar.now = { 875 | day : day, 876 | month : month, 877 | year : year 878 | }; 879 | }, 880 | 881 | HandleInput: function(tokens, msg, isGM){ 882 | var options = AdvancedCalendar._GetOptionsFromTokens(tokens); 883 | tokens=_.filter(tokens, function(tok){ 884 | return null == tok.match(/^--/); 885 | }); 886 | var cmd = tokens[0] || 'month'; 887 | switch (cmd) 888 | { 889 | /* Show the current month calendar */ 890 | case 'month': 891 | AdvancedCalendar.ShowMonth(state.AdvancedCalendar.now,options); 892 | break; 893 | 894 | /* Show the current year calendar */ 895 | case 'year': 896 | AdvancedCalendar.ShowYear(state.AdvancedCalendar.now,options); 897 | break; 898 | 899 | /* Show todays info in detail */ 900 | case 'today': 901 | AdvancedCalendar.ShowDate(state.AdvancedCalendar.now,options); 902 | break; 903 | 904 | /* Advance the current date by a number of days (1 of argument is ommited) */ 905 | case 'next': 906 | if (!isGM) return; 907 | 908 | var days=tokens[1] || 1; 909 | AdvancedCalendar.AdvanceDays(days); 910 | AdvancedCalendar.ShowDate(state.AdvancedCalendar.now,options); 911 | break; 912 | 913 | /* Go back a number of days (1 of argument is ommited) in the current date */ 914 | case 'prev': 915 | if (!isGM) return; 916 | 917 | var days=tokens[1] || 1; 918 | AdvancedCalendar.RemoveDays(days); 919 | AdvancedCalendar.ShowDate(state.AdvancedCalendar.now,options); 920 | break; 921 | 922 | /* Go to a specific date using the parameters according to DD MM YYYY */ 923 | case 'goto': 924 | if (!isGM) return; 925 | 926 | var day = parseInt(tokens[1]); 927 | var month = parseInt(tokens[2] || state.AdvancedCalendar.now.month); 928 | var year = parseInt(tokens[3] || state.AdvancedCalendar.now.year); 929 | AdvancedCalendar.GoToDate(day, month, year); 930 | AdvancedCalendar.ShowDate(state.AdvancedCalendar.now,options); 931 | break; 932 | 933 | case 'see': 934 | var day = parseInt(tokens[1]); 935 | var month = parseInt(tokens[2] || state.AdvancedCalendar.now.month); 936 | var year = parseInt(tokens[3] || state.AdvancedCalendar.now.year); 937 | var seedate = { 938 | day : day, 939 | month : month, 940 | year : year 941 | }; 942 | 943 | AdvancedCalendar.ShowFutureDate(seedate, options); 944 | break; 945 | case 'log': 946 | var mindex = msg.content.indexOf('-m '); 947 | if (mindex <0) return; 948 | 949 | var logdata = msg.content.substring(mindex +3); 950 | log(logdata); 951 | AdvancedCalendar.AddLog(state.AdvancedCalendar.now, msg.who, logdata, false); 952 | break; 953 | case 'note': 954 | if (!isGM) return; 955 | 956 | var mindex = msg.content.indexOf('-m '); 957 | if (mindex <0) return; 958 | 959 | var logdata = msg.content.substring(mindex +3); 960 | log(logdata); 961 | AdvancedCalendar.AddLog(state.AdvancedCalendar.now, msg.who, logdata, true); 962 | break; 963 | 964 | case 'weather': 965 | if (!isGM) return; 966 | 967 | AdvancedCalendar.ChangeWeather(state.AdvancedCalendar.now,options); 968 | AdvancedCalendar.ShowDateAsWhisper(msg.who, state.AdvancedCalendar.now, options) 969 | break; 970 | } 971 | }, 972 | 973 | RegisterEventHandlers: function() { 974 | on("chat:message", function (msg) { 975 | // Exit if not an api command 976 | if (msg.type != "api") return; 977 | 978 | // clean up message bits. 979 | msg.who = msg.who.replace(" (GM)", ""); 980 | msg.content = msg.content.replace("(GM) ", ""); 981 | var isGM = playerIsGM(msg.playerid); 982 | 983 | log(msg.content); 984 | 985 | var tokenized = msg.content.split(" "); 986 | var command = tokenized[0]; 987 | 988 | switch(command) 989 | { 990 | case "!cal": 991 | case "!calendar": 992 | { 993 | AdvancedCalendar.HandleInput(_.rest(tokenized), msg, isGM); 994 | } 995 | break; 996 | 997 | case "!s": 998 | { 999 | sendChat('', 1000 | '/direct
state.AdvancedCalendar
'
1001 |                             +JSON.stringify(state.AdvancedCalendar,undefined,"   ").replace(/\n/g,'
') 1002 | +"
" ); 1003 | } 1004 | break; 1005 | 1006 | case "!t": 1007 | { 1008 | AdvancedCalendar._GetWeekDayForDate(state.AdvancedCalendar.now); 1009 | } 1010 | break; 1011 | } 1012 | }); 1013 | } 1014 | }; 1015 | 1016 | on("ready",function(){ 1017 | log("WeatherRandomGenerator - Version " + WeatherRandomGenerator.version); 1018 | WeatherRandomGenerator.CheckInstall(); 1019 | WeatherRandomGenerator.RegisterEventHandlers(); 1020 | 1021 | 1022 | log("AdvancedCalendar - Version " + AdvancedCalendar.version); 1023 | log("Installing " + CALENDAR.name + "..."); 1024 | AdvancedCalendar.CheckInstall(CALENDAR); 1025 | AdvancedCalendar.RegisterEventHandlers(); 1026 | }); --------------------------------------------------------------------------------