├── image.png ├── examples ├── img │ ├── greenheartOne.png │ ├── greenheartOne.scale-100.png │ ├── greenheartOne.scale-140.png │ └── greenheartOne.scale-180.png └── examples.js ├── package.json ├── api ├── events.js ├── research.js ├── platforms.js ├── topics.js └── persistence.js ├── README.md ├── .gitattributes ├── LICENSE ├── modAPI.js ├── helpers └── checks.js └── .gitignore /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenheartgames/gdt-modAPI/HEAD/image.png -------------------------------------------------------------------------------- /examples/img/greenheartOne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenheartgames/gdt-modAPI/HEAD/examples/img/greenheartOne.png -------------------------------------------------------------------------------- /examples/img/greenheartOne.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenheartgames/gdt-modAPI/HEAD/examples/img/greenheartOne.scale-100.png -------------------------------------------------------------------------------- /examples/img/greenheartOne.scale-140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenheartgames/gdt-modAPI/HEAD/examples/img/greenheartOne.scale-140.png -------------------------------------------------------------------------------- /examples/img/greenheartOne.scale-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenheartgames/gdt-modAPI/HEAD/examples/img/greenheartOne.scale-180.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "id" : "gdt-modAPI", 3 | "name" : "Game Dev Tycoon Mod API", 4 | "version":"0.1.2", 5 | "author":"support@greenheartgames.com", 6 | "url":"https://github.com/greenheartgames/gdt-modAPI", 7 | "description" : "The Official Game Dev Tycoon Mod API which is commonly used by other mods.", 8 | "main" : "./modAPI.js", 9 | "image" : "image.png" 10 | } -------------------------------------------------------------------------------- /api/events.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | GDT.addEvent = function (event) { 3 | if (!Checks.checkPropertiesPresent(event, ['id']) 4 | && (event.notification || event.getNotification)) 5 | return; 6 | if (!Checks.checkUniqueness(event, 'id', DecisionNotifications.modNotifications)) 7 | return; 8 | 9 | DecisionNotifications.modNotifications.push(event); 10 | } 11 | })(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Official Game Dev Tycoon Modding API mod 2 | 3 | This is the official Modding API mod that ships with Game Dev Tycoon version 1.5.0+ 4 | 5 | It is a set of [add-on methods and documentation](https://github.com/greenheartgames/gdt-modAPI/wiki "Wiki") to make the implementation of other mods easier. 6 | 7 | Pull requests are more than welcome. 8 | 9 | ## Wiki 10 | 11 | Please check the official documentation at: 12 | 13 | [https://github.com/greenheartgames/gdt-modAPI/wiki](https://github.com/greenheartgames/gdt-modAPI/wiki) 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ### LICENSE 2 | 3 | 2013 Copyright Greenheart Games Pty. Ltd. 4 | 5 | Writing any mods for Game Dev Tycoon requires the acceptance of and compliance with the Game Dev Tycoon Modding Agreement which is available at: 6 | 7 | http://www.greenheartgames.com/legal/game-dev-tycoon-modding-agreement/ 8 | 9 | The agreement also ships with the game. 10 | 11 | As this is a official mod for Game Dev Tycoon this Software follows the guidelines set out in the agreement. Do not use or contribute to this mod if you do not agree to the Modding agreement. 12 | 13 | ### Usage and contributions 14 | While we welcome contributions to this poject, the software, or any derivatives, may only be used in combination with Game Dev Tycoon. 15 | 16 | Any derivatives must link back to the original project. 17 | 18 | If you have any questions please contact legal@greenheartgames.com 19 | -------------------------------------------------------------------------------- /modAPI.js: -------------------------------------------------------------------------------- 1 | var modAPI_GreenheartGames = {}; 2 | (function () { 3 | modAPI_GreenheartGames.path = GDT.getRelativePath(); 4 | //this is the default modding API module that is loaded as the first mod and provides convenience methods for other mods. 5 | //generally methods are added to the global object GDT. 6 | var ready = function () { 7 | //example calls 8 | 9 | //Examples.addTopic(); 10 | //Examples.addPlatform(); 11 | //Examples.addEvent(); 12 | //Examples.addResearch(); 13 | }; 14 | 15 | var error = function () { 16 | }; 17 | 18 | GDT.loadJs([modAPI_GreenheartGames.path + '/helpers/checks.js', 19 | modAPI_GreenheartGames.path + '/api/persistence.js', 20 | modAPI_GreenheartGames.path + '/api/events.js', 21 | modAPI_GreenheartGames.path + '/api/platforms.js', 22 | modAPI_GreenheartGames.path + '/api/topics.js', 23 | modAPI_GreenheartGames.path + '/api/research.js', 24 | modAPI_GreenheartGames.path + '/examples/examples.js' 25 | ], ready, error); 26 | })(); -------------------------------------------------------------------------------- /api/research.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /* Format 3 | { 4 | id: "Better dialogues", 5 | name: "Better dialogues".localize(), 6 | v: 1, 7 | canResearch: function (company) { 8 | return LevelCalculator.getMissionLevel('Dialogs') > 2; 9 | }, 10 | category: category, 11 | categoryDisplayName: categoryDisplayName 12 | }; 13 | */ 14 | 15 | 16 | var checkResearchV = function (value) { 17 | if ([1, 2, 4, 6, 8, 10, 12, 14].indexOf(value) == -1) { 18 | Checks.error('wrong value v for research item: ' + value); 19 | return false; 20 | } 21 | return true; 22 | }; 23 | 24 | var checkResearchItem = function (research) { 25 | if (!(Checks.checkPropertiesPresent(research, ['id', 'name', 'v', 'category', 'categoryDisplayName']) 26 | && Checks.checkUniqueness(research, 'id', Research.getAllItems()) 27 | && checkResearchV(research.v))) 28 | return false; 29 | 30 | return true; 31 | }; 32 | 33 | GDT.addResearchItem = function (research) { 34 | if (!checkResearchItem(research)) 35 | return; 36 | 37 | Research.engineItems.push(research); 38 | }; 39 | })(); -------------------------------------------------------------------------------- /api/platforms.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var checkPlatform = function (platform) { 4 | if (!(Checks.checkPropertiesPresent(platform, ['id', 'name', 'startAmount', 'unitsSold', 'licencePrize', 'published', 'platformRetireDate', 'developmentCosts', 'genreWeightings', 'audienceWeightings', 'techLevel', 'iconUri']) 5 | && Checks.checkUniqueness(platform, 'id', Platforms.allPlatforms) 6 | && Checks.checkAudienceWeightings(platform.audienceWeightings) 7 | && Checks.checkGenreWeightings(platform.genreWeightings))) 8 | return false; 9 | 10 | if (!(Checks.checkDate(platform.published) 11 | && Checks.checkDate(platform.platformRetireDate))) 12 | return false; 13 | 14 | if (platform.marketPoints) { 15 | for (var i = 0; i < platform.marketPoints.length; i++) { 16 | if (!Checks.checkDate(platform.marketPoints[i].date)) 17 | return false; 18 | } 19 | } 20 | 21 | return true; 22 | }; 23 | 24 | 25 | GDT.addPlatform = function (platform) { 26 | if (!checkPlatform(platform)) 27 | return; 28 | 29 | Platforms.allPlatforms.push(platform); 30 | if (platform.events) { 31 | for (var i = 0; i < platform.events.length; i++) { 32 | GDT.addEvent(platform.events[i]); 33 | } 34 | } 35 | }; 36 | })(); -------------------------------------------------------------------------------- /api/topics.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /* 3 | adds topics to game. 4 | takes array of topic objects defined like 5 | { 6 | id: "Game Dev", //must be unique 7 | name: "Game Dev".localize("game topic"), //user-friendly name 8 | iconUrl: "",//GDT.getRelativePath() + "/images/myImage.png" (see documentation for details) 9 | genreWeightings: [.6, .7, .6, 1, .6, .8], //genreWeightings (see documentation for details) 10 | audienceWeightings: [0.9, 1, 0.7] //audienceWeightings (see documentation for details) 11 | } 12 | 13 | additional option property missionOverrides. Example: 14 | 15 | 16 | 17 | id's must be unique 18 | */ 19 | GDT.addTopics = function (values) { 20 | if (!values) 21 | return; 22 | //filter out invalid topics 23 | for (var i = 0; i < values.length; i++) { 24 | var t = values[i]; 25 | if (!(Checks.checkPropertiesPresent(t, ['name', 'id', 'genreWeightings', 'audienceWeightings']) 26 | && Checks.checkAudienceWeightings(t.audienceWeightings) 27 | && Checks.checkGenreWeightings(t.genreWeightings) 28 | && Checks.checkUniqueness(t, 'id', Topics.topics, true) 29 | && Checks.checkUniqueness(t, 'name', Topics.topics, true))) 30 | continue; 31 | 32 | if (t.missionOverrides 33 | && !Checks.checkMissionOverrides(t.missionOverrides)) { 34 | continue; 35 | } 36 | 37 | //add topic to game. 38 | Topics.topics.push(t); 39 | } 40 | }; 41 | })(); -------------------------------------------------------------------------------- /api/persistence.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var stores = {}; 4 | 5 | /* 6 | provides data access to save game data and settings. 7 | usage: 8 | 9 | var store = GDT.getDataStore("myPluginId"); 10 | store.data["key"]=value; //save game specific settings 11 | store.settings["key"]=value; //app-wide settings 12 | */ 13 | GDT.getDataStore = function (pluginId) { 14 | if (stores.hasOwnProperty(pluginId)) 15 | return stores[pluginId]; 16 | 17 | if (!DataStore.settings.modData) 18 | DataStore.settings.modData = {}; 19 | 20 | if (!DataStore.settings.modData[pluginId]) 21 | DataStore.settings.modData[pluginId] = {}; 22 | 23 | var obj = { 24 | data: {}, 25 | settings: DataStore.settings.modData[pluginId] 26 | }; 27 | 28 | stores[pluginId] = obj; 29 | 30 | return obj; 31 | }; 32 | 33 | //set data property in all stores to modData[plugin-id] value when loading a save game. 34 | GDT.on(GDT.eventKeys.saves.loading, function (e) { 35 | var data = e.data; 36 | var modData = data['modData']; 37 | if (!modData) { 38 | modData = data.modData = {}; 39 | } 40 | for (var id in modData) {//where item is the id of a plugin 41 | if (modData.hasOwnProperty(id)) { 42 | GDT.getDataStore(id).data = modData[id]; 43 | } 44 | } 45 | }); 46 | 47 | //set data property of all existing mod stores to modData[plugin-id] when saving a game. 48 | GDT.on(GDT.eventKeys.saves.saving, function (e) { 49 | var data = e.data; 50 | var modData = data['modData']; 51 | if (!modData) { 52 | modData = data.modData = {}; 53 | } 54 | for (var id in stores) { 55 | if (!stores.hasOwnProperty(id)) 56 | continue; 57 | modData[id] = stores[id].data; 58 | } 59 | }); 60 | 61 | GDT.on(GDT.eventKeys.saves.newGame, function (e) { 62 | for (var key in stores) { 63 | if (stores.hasOwnProperty(key)) { 64 | stores[key].data = {}; 65 | } 66 | } 67 | }); 68 | 69 | })(); -------------------------------------------------------------------------------- /helpers/checks.js: -------------------------------------------------------------------------------- 1 | var Checks = {}; 2 | (function () { 3 | //helper methods to perform common checks 4 | 5 | Checks.error = function (m) { 6 | try { 7 | throw new Error(m); 8 | } 9 | catch (e) { 10 | Logger.LogModError(m, e, "A mod caused an error"); 11 | } 12 | } 13 | 14 | Checks.checkAudienceWeightings = function (w) { 15 | if (!w || w.length < 3 || w.some(function (v) { return v < 0 || v > 1; })) { 16 | Checks.error('audience weigthing is invalid: {0}'.format(w)); 17 | return false; 18 | } 19 | return true; 20 | }; 21 | 22 | Checks.checkGenreWeightings = function (w) { 23 | if (!w || w.length < 6 || w.some(function (v) { return v < 0 || v > 1; })) { 24 | Checks.error('genre weigthing is invalid: {0}'.format(w)); 25 | return false; 26 | } 27 | return true; 28 | }; 29 | 30 | Checks.checkMissionOverrides = function (missionOverrides) { 31 | if (missionOverrides.length < 6 32 | || missionOverrides.some(function (overrides) { 33 | return overrides.length < 6 34 | || overrides.some(function (w) { return w > 1 || w < 0; }); 35 | })) { 36 | Checks.error('invalid missionOverrides: {0}'.format(missionOverrides)); 37 | return false; 38 | } 39 | return true; 40 | }; 41 | 42 | Checks.checkDate = function (date) { 43 | //date is format y/m/w 44 | if (date && date.split) { 45 | var values = date.split("/"); 46 | if (values && values.length == 3) { 47 | if (!values.some(function (v) { return v < 1; })) { 48 | if (values[1] <= 12 49 | && values[2] <= 4) { 50 | return true; 51 | } 52 | } 53 | } 54 | } 55 | Checks.error('date invalid: {0}'.format(date)); 56 | return false; 57 | }; 58 | 59 | 60 | //dynamically check wether a property is present on obj 61 | Checks.checkPropertiesPresent = function (obj, props) { 62 | if (!obj) 63 | return false; 64 | if (!props) 65 | return true; 66 | for (var i = 0; i < props.length; i++) { 67 | var p = props[i]; 68 | if (!p || p.length < 1) 69 | continue; 70 | if (!obj.hasOwnProperty(p)) { 71 | Checks.error('property not set on object: {0}'.format(p)); 72 | return false; 73 | } 74 | } 75 | return true; 76 | }; 77 | 78 | Checks.checkUniqueness = function (obj, prop, values, ignoreError) { 79 | var unique = !values.some(function (v) { return v[prop] == obj[prop]; }); 80 | if (!unique && !ignoreError) { 81 | Checks.error('duplicate value for {0} found: {1}'.format(prop, obj[prop])); 82 | } 83 | return unique; 84 | }; 85 | })(); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /examples/examples.js: -------------------------------------------------------------------------------- 1 | var Examples = {}; 2 | (function () { 3 | Examples.addTopic = function () { 4 | GDT.addTopics([ 5 | { 6 | id: "Assassin", 7 | name: "Assassin".localize("game topic"), 8 | genreWeightings: [1, 1, 1, .6, .6, .6], 9 | audienceWeightings: [0.6, 0.8, 1] 10 | } 11 | ]); 12 | }; 13 | 14 | Examples.addPlatform = function () { 15 | var icon = GDT.getRelativePath() + '/examples/img/greenheartOne.png'; 16 | GDT.addPlatform( 17 | { 18 | id: 'Greenheart One', 19 | name: 'Greenheart One', 20 | company: 'Greenheart Games', 21 | startAmount: 0.15, 22 | unitsSold: 0.358, 23 | licencePrize: 5000, 24 | published: '1/3/4', 25 | platformRetireDate: '4/6/2', 26 | developmentCosts: 10000, 27 | genreWeightings: [0.9, 1, 1, 0.9, 1, 0.7], 28 | audienceWeightings: [0.9, 1, 0.8], 29 | techLevel: 1, 30 | iconUri: icon, 31 | events: [ 32 | { 33 | id: '10537DA1-58F1-4F23-8854-F1E2621933BF', 34 | date: '1/2/1', 35 | getNotification: function (company) { 36 | return new Notification({ 37 | header: "Industry News".localize(), 38 | text: "Coming out of nowhere a company called Greenheart Games has announced that it will publish a new game console called the Greenheart One {0}.".localize().format(General.getETADescription('1/2/1', '1/3/4')), 39 | image: icon 40 | }); 41 | } 42 | } 43 | ] 44 | }); 45 | }; 46 | 47 | Examples.addEvent = function () { 48 | /* 49 | example event: 50 | when does it fire: random event, only in the first office when a game is in development 51 | what happens: neighbours kid spies on game dev, resulting in several options. 52 | */ 53 | 54 | var eventId = "F413351E-2108-4967-A989-A7E98D4DEED5";//any string, but needs to be globally unique 55 | 56 | var myRandomEvent = { 57 | id: eventId, 58 | isRandom: true, //if you want to test this event, you can set this to false and it will trigger during dev. of the first game. 59 | maxTriggers: 1, 60 | trigger: function (company) { 61 | //only in first office and only while a game is in development. 62 | //most events that fire during game-dev work better if they don't fire right at the start or right at the end, that's why we use isGameProgressBetween 63 | return company.currentLevel == 1 && company.isGameProgressBetween(0.2, 0.9); 64 | }, 65 | //because we dynamically create the notification every time the event triggers, we use getNotification 66 | getNotification: function (company) { 67 | var game = company.currentGame; 68 | 69 | var msg = "It seems that kids in the neighbourhood have started chatting about your upcoming game {0}. Rumour has it, that Billy, your neighbours kid, snuck into the garage and spied on some of the design papers.{n}How he managed to do this is a mystery. You could swear you were sitting in the garage the entire time!\nHow do you want to react?\n\nYou could talk to the parents to get him punished, ignore the incident or maybe invite some of the neighbours over to show them more of the game." 70 | .localize().format(game.title); 71 | //notice how we break up the story in two screens by using {n}. This creates a better flow than having one longer block of text. 72 | //Also, since this is a story with multiple options and the buttons can only hold a small amount of text, we explain the options beforehand. 73 | 74 | //the chatting among kids, creates a bit of hype. 75 | //since the event isn't delayed we can do this directly on the company, otherwise we could call adjustHype() on the notification object to schedule the effect with the notification. 76 | company.adjustHype(5 + 10 * company.getRandom());//increase hype between 5 and 15. 77 | 78 | return new Notification({ 79 | sourceId: eventId,//this is important, otherwise nothing will happen when the player selects an option. 80 | header: "Billy, the kid".localize(),//granted, this is a silly header. 81 | text: msg, 82 | options: ["Talk to parents".localize(), "Ignore incident".localize(), "Invite over".localize()]//maximum of three choices 83 | }); 84 | }, 85 | complete: function (decision) { 86 | //decision is a number and will correspond to the index of the option that was chosen. 87 | //0=talk to parents, 1=ignore incident, 2=invite over 88 | //it's best if every decision has a different outcome 89 | 90 | var company = GameManager.company;//we fetch the company object for use later. 91 | 92 | if (decision === 0) {//talk to parents 93 | //we create a new, simple notification to tell the outcome. no sourceId or options are necessary this time. 94 | var n = new Notification({ 95 | header: "Billy, the kid".localize(),//keep the header consistent with the prior part of the story 96 | text: "You talk to the parents about Billy's actions and they promise it won't happen again.".localize() 97 | }); 98 | n.adjustHype(5 + 10 * company.getRandom());//increase hype between 5 and 15. 99 | 100 | company.activeNotifications.addRange(n.split()); //since this notificaton should be shown immediately (not one second later) we insert it into activeNotifications. calling .split() ist just good practice in case we use {n} inside the notification. 101 | return; 102 | } 103 | if (decision === 1) {//ignore incident 104 | //nothing happens at first, but in a few weeks Billy again breaks in... 105 | var n = new Notification({ 106 | header: "Vanished documents".localize(), 107 | text: "You were working on some intricate design documents the other day but now you can't find them anymore. Small foot prints on the floor suggest that someone might have taken them.\nUnfortunately you have to recreate the documents (-500 cr.) - This might have been Billy's work".localize(), 108 | weeksUntilFired: 1 + 2 * company.getRandom() 109 | }); 110 | n.adjustCash(-500, "restoring documents"); 111 | company.notifications.push(n);//this notification isn't shown immediately so we add it to the normal company.notifications array. 112 | return; 113 | } 114 | if (decision === 2) {//invite him over 115 | var n = new Notification({ 116 | header: "Billy, the kid".localize(),//keep the header consistent with the prior part of the story 117 | text: "You invite Billy, his parents and a couple of other interested neighbours over and show them the game in-progress. The kids are super-excited and for weeks you hear them talk about it afterwards.".localize() 118 | }); 119 | n.adjustHype(15 + 25 * company.getRandom());//increase hype between 15 and 40 120 | company.activeNotifications.addRange(n.split()); //since this notificaton should be shown immediately (not one second later) we insert it into activeNotifications. calling .split() ist just good practice in case we use {n} inside the notification. 121 | return; 122 | } 123 | } 124 | }; 125 | 126 | GDT.addEvent(myRandomEvent); 127 | }; 128 | 129 | Examples.addResearch = function () { 130 | GDT.addResearchItem( 131 | { 132 | id: "Swift Loading", //Must be unique 133 | name: "Swift loading".localize(), //Display name 134 | v: 4, //Tech level 135 | canResearch: function (company) { 136 | return LevelCalculator.getMissionLevel('Level Design') > 5 //The "Level Design" level has to be at least 6 137 | }, 138 | category: "Level Design", 139 | categoryDisplayName: "Level Design".localize() 140 | }); 141 | }; 142 | })(); --------------------------------------------------------------------------------