Showing
100 changed files
with
306 additions
and
187 deletions
Too many changes to show.
To preserve performance only 100 of 100+ files are displayed.
... | ... | @@ -11,4 +11,14 @@ out.avi |
11 | 11 | out.ogv |
12 | 12 | te4_log_web.txt |
13 | 13 | t-engine |
14 | -tengine_doc | |
\ No newline at end of file | ||
14 | +tengine_doc | |
15 | +game/modules/pits/ | |
16 | +game/modules/uaws/ | |
17 | +game/modules/bare_minimum/ | |
18 | +game/modules/bone/ | |
19 | +game/modules/demo/ | |
20 | +game/modules/particles_editor/ | |
21 | +steam_appid.txt | |
22 | +oprofile_data/ | |
23 | +game/modules/tome/data/gfx/ts-gfx* | |
24 | +game/engines/default/modules/boot/data/gfx/ts-gfx* | ... | ... |
... | ... | @@ -29,12 +29,19 @@ if __SELFEXE then |
29 | 29 | print("SelfExe gave us app directory of:", dir) |
30 | 30 | fs.mount(dir..fs.getPathSeparator().."game"..fs.getPathSeparator().."thirdparty", "/", true) |
31 | 31 | fs.mount(dir..fs.getPathSeparator().."game", "/", true) |
32 | + fs.setPathAllowed(dir..fs.getPathSeparator().."game", false) | |
32 | 33 | else |
33 | 34 | print("No SelfExe, using basic path") |
34 | 35 | fs.mount("game"..fs.getPathSeparator().."thirdparty", "/", true) |
35 | 36 | fs.mount("game", "/", true) |
36 | 37 | end |
37 | 38 | |
39 | +fs.setPathAllowed(fs.getRealPath("/engines/"), false) | |
40 | +fs.setPathAllowed(fs.getRealPath("/thirdparty/"), false) | |
41 | +fs.setPathAllowed(fs.getRealPath("/addons/"), true) | |
42 | +if fs.getRealPath("/dlcs/") then fs.setPathAllowed(fs.getRealPath("/dlcs/"), true) end | |
43 | +fs.setPathAllowed(fs.getRealPath("/modules/"), true) | |
44 | + | |
38 | 45 | -- Look for a core |
39 | 46 | function get_core(coretype, id) |
40 | 47 | coretype = coretype or "te4core" | ... | ... |
... | ... | @@ -66,6 +66,16 @@ newoption { |
66 | 66 | } |
67 | 67 | |
68 | 68 | newoption { |
69 | + trigger = "discord", | |
70 | + description = "Discord integration" | |
71 | +} | |
72 | + | |
73 | +newoption { | |
74 | + trigger = "discord-nolib", | |
75 | + description = "Dont build discord lib" | |
76 | +} | |
77 | + | |
78 | +newoption { | |
69 | 79 | trigger = "web-awesomium", |
70 | 80 | description = "Use awesomium embedded browser as the webcore" |
71 | 81 | } | ... | ... |
... | ... | @@ -38,6 +38,7 @@ project "TEngine" |
38 | 38 | files { "../steamworks/luasteam.c", } |
39 | 39 | end |
40 | 40 | links { "physfs", "lua".._OPTIONS.lua, "fov", "luasocket", "luaprofiler", "lpeg", "tcodimport", "lxp", "expatstatic", "luamd5", "luazlib", "luabitop", "te4-bzip" } |
41 | + if _OPTIONS.discord then defines { "DISCORD_TE4" } end | |
41 | 42 | defines { "_DEFAULT_VIDEOMODE_FLAGS_='SDL_HWSURFACE|SDL_DOUBLEBUF'" } |
42 | 43 | defines { [[TENGINE_HOME_PATH='".t-engine"']], "TE4CORE_VERSION="..TE4CORE_VERSION } |
43 | 44 | buildoptions { "-O3" } |
... | ... | @@ -597,3 +598,27 @@ end |
597 | 598 | if _OPTIONS.steam then |
598 | 599 | dofile("../steamworks/build/steam-code.lua") |
599 | 600 | end |
601 | + | |
602 | +if _OPTIONS.discord and not _OPTIONS['discord-nolib'] then | |
603 | +project "te4-discord" | |
604 | + configuration "linux" | |
605 | + kind "SharedLib" | |
606 | + language "C++" | |
607 | + targetname "discord-rpc" | |
608 | + buildoptions { "-std=gnu++11" } | |
609 | + | |
610 | + includedirs { "../src/discord-rpc/include/", "../src/discord-rpc/rapidjson/", } | |
611 | + | |
612 | + defines { "DISCORD_DYNAMIC_LIB" } | |
613 | + | |
614 | + files { "../src/discord-rpc/src/discord-rpc.cpp", "../src/discord-rpc/src/rpc_connection.cpp", "../src/discord-rpc/src/serialization.cpp", } | |
615 | + files { "../src/discord-rpc/src/connection_unix.cpp", "../src/discord-rpc/src/discord_register_linux.cpp", } | |
616 | + | |
617 | + configuration "windows" | |
618 | + kind "SharedLib" | |
619 | + -- Empty | |
620 | + | |
621 | + configuration "macosx" | |
622 | + kind "SharedLib" | |
623 | + -- Empty | |
624 | +end | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -73,7 +73,7 @@ end |
73 | 73 | -- modules should update this as needed |
74 | 74 | _M.clone_nodes = {player=false, x=false, y=false, |
75 | 75 | fov_computed=false, fov={v={actors={}, actors_dist={}}}, distance_map={v={}}, |
76 | - _mo=false, _last_mo=false, add_mos=false, add_displays=false, | |
76 | + _mo=false, _last_mo=false, add_displays=false, | |
77 | 77 | shader=false, shader_args=false, |
78 | 78 | } |
79 | 79 | --- cloneActor default fields (merged by _M.cloneActor with cloneCustom) |
... | ... | @@ -90,7 +90,15 @@ _M.clone_copy = nil |
90 | 90 | function _M:cloneActor(post_copy, alt_nodes) |
91 | 91 | alt_nodes = table.merge(alt_nodes or {}, self.clone_nodes, true) |
92 | 92 | if post_copy or self.clone_copy then post_copy = post_copy or {} table.update(post_copy, self.clone_copy or {}, true) end |
93 | - local a = self:cloneCustom(alt_nodes, post_copy) | |
93 | + -- Clone all except sub-actors which need to simply reference the same ones | |
94 | + local a = self:cloneCustom(alt_nodes, function(d) return not d:isClassName("mod.class.Actor") end, post_copy) | |
95 | + -- Handle add_displays as a special case | |
96 | + if self.add_displays then | |
97 | + a.add_displays = {} | |
98 | + for i, d in ipairs(self.add_displays) do | |
99 | + table.insert(a.add_displays, d:cloneFull()) | |
100 | + end | |
101 | + end | |
94 | 102 | a:removeAllMOs() |
95 | 103 | return a, post_copy |
96 | 104 | end | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -43,13 +43,17 @@ function _M:init(name, npc, player, data) |
43 | 43 | |
44 | 44 | local f, err = loadfile(self:getChatFile(name)) |
45 | 45 | if not f and err then error(err) end |
46 | - setfenv(f, setmetatable({ | |
46 | + local env = setmetatable({ | |
47 | + cur_chat = self, | |
47 | 48 | setDialogWidth = function(w) self.force_dialog_width = w end, |
48 | 49 | newChat = function(c) self:addChat(c) end, |
49 | - }, {__index=data})) | |
50 | + setTextFont = function(font, size) self.dialog_text_font = {font, size} end, | |
51 | + setAnswerFont = function(font, size) self.dialog_answer_font = {font, size} end, | |
52 | + }, {__index=data}) | |
53 | + setfenv(f, env) | |
50 | 54 | self.default_id = f() |
51 | 55 | |
52 | - self:triggerHook{"Chat:load", data=data} | |
56 | + self:triggerHook{"Chat:load", data=data, env=env} | |
53 | 57 | end |
54 | 58 | |
55 | 59 | --- Get chat file |
... | ... | @@ -100,7 +104,10 @@ function _M:invoke(id) |
100 | 104 | if self.npc.onChat then self.npc:onChat() end |
101 | 105 | if self.player.onChat then self.player:onChat() end |
102 | 106 | |
103 | - local d = engine.dialogs.Chat.new(self, id or self.default_id, self.force_dialog_width or 500) | |
107 | + local hd = {"Chat:invoke", id = id or self.default_id } | |
108 | + self:triggerHook(hd) | |
109 | + | |
110 | + local d = engine.dialogs.Chat.new(self, hd.id, self.force_dialog_width or 500) | |
104 | 111 | game:registerDialog(d) |
105 | 112 | return d |
106 | 113 | end | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -32,17 +32,18 @@ frame_oy2 = 15 |
32 | 32 | --- @string text |
33 | 33 | -- @int[opt=60] dur |
34 | 34 | -- @param[opt=colors.Black] color |
35 | -function _M:init(text, dur, color) | |
35 | +function _M:init(text, dur, color, font) | |
36 | 36 | self.text = text |
37 | 37 | self.dur = dur or 60 |
38 | 38 | self.color = color or colors.BLACK |
39 | + self.use_font = font | |
39 | 40 | |
40 | - Base.init(self, {font = {"/data/font/DroidSans-Bold.ttf", 16}}) | |
41 | + Base.init(self, {font = self.use_font or {"/data/font/DroidSans-Bold.ttf", 16}}) | |
41 | 42 | end |
42 | 43 | |
43 | 44 | --- on loaded |
44 | 45 | function _M:loaded() |
45 | - Base.init(self, {font = {"/data/font/DroidSans-Bold.ttf", 16}}) | |
46 | + Base.init(self, {font = self.use_font or {"/data/font/DroidSans-Bold.ttf", 16}}) | |
46 | 47 | end |
47 | 48 | |
48 | 49 | --- Serialization | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -1206,6 +1206,10 @@ function _M:loadList(file, no_default, res, mod, loaded) |
1206 | 1206 | load = function(f, new_mod) |
1207 | 1207 | self:loadList(f, no_default, res, new_mod or mod, loaded) |
1208 | 1208 | end, |
1209 | + loadIfNot = function(f, new_mod) | |
1210 | + if loaded[f] then return end | |
1211 | + self:loadList(f, no_default, res, new_mod or mod, loaded) | |
1212 | + end, | |
1209 | 1213 | loadList = function(f, new_mod, list, loaded) |
1210 | 1214 | return self:loadList(f, no_default, list, new_mod or mod, loaded) |
1211 | 1215 | end, | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -475,7 +475,7 @@ function _M:addonMD5(add, base) |
475 | 475 | print("[MODULE LOADER] addon ", add.short_name, " MD5", fmd5, "computed in ", core.game.getTime() - t, vbase) |
476 | 476 | |
477 | 477 | if __module_extra_info.compute_md5_only then |
478 | - local f = io.open(__module_extra_info.compute_md5_only, "a") | |
478 | + local f = fs.open(__module_extra_info.compute_md5_only, "a") | |
479 | 479 | f:write(("%s : addon[%s] md5\n"):format(fmd5, add.version_name)) |
480 | 480 | f:close() |
481 | 481 | end |
... | ... | @@ -982,7 +982,7 @@ function _M:instanciate(mod, name, new_game, no_reboot, extra_module_info) |
982 | 982 | print("[MODULE LOADER] module MD5", module_md5, "computed in ", core.game.getTime() - t) |
983 | 983 | |
984 | 984 | if __module_extra_info.compute_md5_only then |
985 | - local f = io.open(__module_extra_info.compute_md5_only, "w") | |
985 | + local f = fs.open(__module_extra_info.compute_md5_only, "w") | |
986 | 986 | f:write(("%s : module[%s] md5\n"):format(module_md5, mod.version_string)) |
987 | 987 | f:close() |
988 | 988 | end |
... | ... | @@ -1019,6 +1019,7 @@ function _M:instanciate(mod, name, new_game, no_reboot, extra_module_info) |
1019 | 1019 | profile:incrLoadProfile(mod) |
1020 | 1020 | profile:currentCharacter(mod.full_version_string, "game did not tell us") |
1021 | 1021 | |
1022 | + | |
1022 | 1023 | UIBase:clearCache() |
1023 | 1024 | |
1024 | 1025 | -- Some requires cleanup, to correctly let modules apply settings | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -205,6 +205,9 @@ function _M:getSubtypeOrder() |
205 | 205 | return self.subtype or "" |
206 | 206 | end |
207 | 207 | |
208 | +--- Describe requirements flags naming | |
209 | +_M.requirement_flags_names = {} | |
210 | + | |
208 | 211 | --- Describe requirements |
209 | 212 | function _M:getRequirementDesc(who) |
210 | 213 | local req = rawget(self, "require") |
... | ... | @@ -212,6 +215,19 @@ function _M:getRequirementDesc(who) |
212 | 215 | |
213 | 216 | local str = tstring{"Requires:", true} |
214 | 217 | |
218 | + if req.flag then | |
219 | + for _, flag in ipairs(req.flag) do | |
220 | + if type(flag) == "table" then | |
221 | + local name = self.requirement_flags_names[flag[1]] or flag[1] | |
222 | + local c = (who:attr(flag[1]) and who:attr(flag[1]) >= flag[2]) and {"color", 0x00,0xff,0x00} or {"color", 0xff,0x00,0x00} | |
223 | + str:add(c, "- ", ("%s (level %d)"):format(name, flag[2]), {"color", "LAST"}, true) | |
224 | + else | |
225 | + local name = self.requirement_flags_names[flag] or flag | |
226 | + local c = who:attr(flag) and {"color", 0x00,0xff,0x00} or {"color", 0xff,0x00,0x00} | |
227 | + str:add(c, "- ", ("%s"):format(name), {"color", "LAST"}, true) | |
228 | + end | |
229 | + end | |
230 | + end | |
215 | 231 | if req.stat then |
216 | 232 | for s, v in pairs(req.stat) do |
217 | 233 | local c = (who:getStat(s) >= v) and {"color", 0x00,0xff,0x00} or {"color", 0xff,0x00,0x00} | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -23,6 +23,7 @@ local url = require "socket.url" |
23 | 23 | local ltn12 = require "ltn12" |
24 | 24 | local Dialog = require "engine.ui.Dialog" |
25 | 25 | local UserChat = require "engine.UserChat" |
26 | +local sha1 = require("sha1").sha1 | |
26 | 27 | require "Json2" |
27 | 28 | |
28 | 29 | --- Handles the player profile, possibly online |
... | ... | @@ -98,6 +99,12 @@ function _M:start() |
98 | 99 | self:loadGenericProfile() |
99 | 100 | |
100 | 101 | if self.generic.online and self.generic.online.login and self.generic.online.pass then |
102 | + -- Convert to encrypted pass | |
103 | + if not self.generic.online.v2 then | |
104 | + self.generic.online.pass = sha1(self.generic.online.pass) | |
105 | + self:saveGenericProfile("online", {login=self.generic.online.login, pass=self.generic.online.pass, v2=true}) | |
106 | + end | |
107 | + | |
101 | 108 | self.login = self.generic.online.login |
102 | 109 | self.pass = self.generic.online.pass |
103 | 110 | self:tryAuth() |
... | ... | @@ -130,7 +137,7 @@ function _M:mountProfile(online, module) |
130 | 137 | fs.mkdir(string.format("/profiles/%s/generic/", online and "online" or "offline")) |
131 | 138 | if module then fs.mkdir(string.format("/profiles/%s/modules/%s", online and "online" or "offline", module)) end |
132 | 139 | |
133 | - local path = engine.homepath.."/profiles/"..(online and "online" or "offline") | |
140 | + local path = engine.homepath..fs.getPathSeparator().."profiles"..fs.getPathSeparator()..(online and "online" or "offline") | |
134 | 141 | fs.mount(path, "/current-profile") |
135 | 142 | print("[PROFILE] mounted ", online and "online" or "offline", "on /current-profile") |
136 | 143 | fs.setWritePath(path) |
... | ... | @@ -138,7 +145,7 @@ function _M:mountProfile(online, module) |
138 | 145 | return restore |
139 | 146 | end |
140 | 147 | function _M:umountProfile(online, pop) |
141 | - local path = engine.homepath.."/profiles/"..(online and "online" or "offline") | |
148 | + local path = engine.homepath..fs.getPathSeparator().."profiles"..fs.getPathSeparator()..(online and "online" or "offline") | |
142 | 149 | fs.umount(path) |
143 | 150 | print("[PROFILE] unmounted ", online and "online" or "offline", "from /current-profile") |
144 | 151 | |
... | ... | @@ -147,9 +154,9 @@ end |
147 | 154 | |
148 | 155 | -- Define the fields that are sync'ed online, and how they are sync'ed |
149 | 156 | local generic_profile_defs = { |
150 | - firstrun = {nosync=true, {firstrun="number"}, receive=function(data, save) save.firstrun = data.firstrun end }, | |
151 | - online = {nosync=true, {login="string:40", pass="string:40"}, receive=function(data, save) save.login = data.login save.pass = data.pass end }, | |
152 | - onlinesteam = {nosync=true, {autolog="boolean"}, receive=function(data, save) save.autolog = data.autolog end }, | |
157 | + firstrun = {nosync=true, no_sync=true, {firstrun="number"}, receive=function(data, save) save.firstrun = data.firstrun end }, | |
158 | + online = {nosync=true, no_sync=true, {login="string:40", pass="string:40", v2="number"}, receive=function(data, save) save.login = data.login save.pass = data.pass save.v2 = data.v2 end }, | |
159 | + onlinesteam = {nosync=true, no_sync=true, {autolog="boolean"}, receive=function(data, save) save.autolog = data.autolog end }, | |
153 | 160 | modules_played = { {name="index:string:30"}, {time_played="number"}, receive=function(data, save) max_set(save, data.name, data, "time_played") end, export=function(env) for k, v in pairs(env) do add{name=k, time_played=v} end end }, |
154 | 161 | modules_loaded = { {name="index:string:30"}, {nb="number"}, receive=function(data, save) max_set(save, data.name, data, "nb") end, export=function(env) for k, v in pairs(env) do add{name=k, nb=v} end end }, |
155 | 162 | } |
... | ... | @@ -425,14 +432,16 @@ function _M:checkFirstRun() |
425 | 432 | end |
426 | 433 | |
427 | 434 | function _M:performlogin(login, pass) |
435 | + pass = sha1(pass) | |
436 | + | |
428 | 437 | self.login=login |
429 | 438 | self.pass=pass |
430 | 439 | print("[ONLINE PROFILE] attempting log in ", self.login) |
431 | 440 | self.auth_tried = nil |
432 | 441 | self:tryAuth() |
433 | 442 | self:waitFirstAuth() |
434 | - if (profile.auth) then | |
435 | - self:saveGenericProfile("online", {login=login, pass=pass}) | |
443 | + if profile.auth then | |
444 | + self:saveGenericProfile("online", {login=login, pass=pass, v2=true}) | |
436 | 445 | self:getConfigs("generic") |
437 | 446 | self:syncOnline("generic") |
438 | 447 | end |
... | ... | @@ -477,6 +486,9 @@ function _M:popEvent(specific) |
477 | 486 | end |
478 | 487 | |
479 | 488 | function _M:waitEvent(name, cb, wait_max) |
489 | + -- Dont try as it would fail and we'd fait for nothing | |
490 | + if config.settings.disable_all_connectivity then return end | |
491 | + | |
480 | 492 | -- Wait anwser, this blocks thegame but cant really be avoided :/ |
481 | 493 | local stop = false |
482 | 494 | local first = true |
... | ... | @@ -509,6 +521,9 @@ function _M:noMoreAuthWait() |
509 | 521 | end |
510 | 522 | |
511 | 523 | function _M:waitFirstAuth(timeout) |
524 | + -- Dont try as it would fail and we'd fait for nothing | |
525 | + if config.settings.disable_all_connectivity then return end | |
526 | + | |
512 | 527 | if self.no_more_wait_auth then return end |
513 | 528 | if self.auth_tried and self.auth_tried >= 1 then return end |
514 | 529 | if not self.waiting_auth then return end |
... | ... | @@ -564,7 +579,7 @@ function _M:eventGetNews(e) |
564 | 579 | end |
565 | 580 | |
566 | 581 | function _M:eventIncrLogConsume(e) |
567 | - local module = game.__mod_info.short_name | |
582 | + local module = type(game) == "table" and game.__mod_info.short_name | |
568 | 583 | if not module then return end |
569 | 584 | print("[PROFILE] Server accepted our incr log, deleting") |
570 | 585 | local pop = self:mountProfile(true, module) |
... | ... | @@ -592,6 +607,13 @@ function _M:eventGetConfigs(e) |
592 | 607 | end |
593 | 608 | |
594 | 609 | function _M:eventPushCode(e) |
610 | + if not config.settings.allow_online_events then | |
611 | + if e.return_uuid then | |
612 | + core.profile.pushOrder(string.format("o='CodeReturn' uuid=%q data=%q", e.return_uuid, table.serialize{error='user disabled events, refusing to load code'})) | |
613 | + end | |
614 | + return | |
615 | + end | |
616 | + | |
595 | 617 | local f, err = loadstring(e.code) |
596 | 618 | if not f then |
597 | 619 | if e.return_uuid then |
... | ... | @@ -649,6 +671,9 @@ function _M:getNews(callback, steam) |
649 | 671 | end |
650 | 672 | |
651 | 673 | function _M:tryAuth() |
674 | + -- Dont try as it would fail and we'd fait for nothing | |
675 | + if config.settings.disable_all_connectivity then return end | |
676 | + | |
652 | 677 | print("[ONLINE PROFILE] auth") |
653 | 678 | self.auth_last_error = nil |
654 | 679 | if self.steam_token then |
... | ... | @@ -1019,7 +1044,8 @@ function _M:isDonator(s) |
1019 | 1044 | end |
1020 | 1045 | |
1021 | 1046 | function _M:allowDLC(dlc) |
1022 | - if core.steam then if core.steam.checkDLC(dlc[2]) then return true end end | |
1023 | - if self.auth and self.auth.dlcs and self.auth.dlcs[dlc[1]] then return true end | |
1024 | - return false | |
1047 | + -- if core.steam then if core.steam.checkDLC(dlc[2]) then return true end end | |
1048 | + -- if self.auth and self.auth.dlcs and self.auth.dlcs[dlc[1]] then return true end | |
1049 | + -- return false | |
1050 | + return true | |
1025 | 1051 | end | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -74,6 +74,13 @@ function _M:isEnded() |
74 | 74 | if self.status == DONE or self.status == FAILED then return true else return false end |
75 | 75 | end |
76 | 76 | |
77 | +--- Checks if the quest is ended in success | |
78 | +-- @return[1] false if quest didn't end | |
79 | +-- @return[2] true if quest ended | |
80 | +function _M:isSuccess() | |
81 | + if self.status == DONE then return true else return false end | |
82 | +end | |
83 | + | |
77 | 84 | --- Check for an quest property |
78 | 85 | -- If not a function it returns it directly, otherwise it calls the function |
79 | 86 | -- with the extra parameters | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -163,15 +163,22 @@ function _M:unregisterTalkEvents(fct) |
163 | 163 | self.on_event[fct] = nil |
164 | 164 | end |
165 | 165 | |
166 | +function _M:getUserColor(e) | |
167 | + local name = e.name | |
168 | + local color = colors.WHITE | |
169 | + if e.status == 'dev' then color = colors.CRIMSON name = "{dev} "..name | |
170 | + elseif e.status == 'mod' then color = colors.GOLD name = "[mod] "..name | |
171 | + elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
172 | + elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
173 | + e.color_name = color | |
174 | + return color, name | |
175 | +end | |
176 | + | |
166 | 177 | function _M:event(e) |
167 | 178 | if not profile.auth then return end |
168 | 179 | if e.se == "Talk" then |
169 | 180 | e.msg = e.msg:removeColorCodes():gsub("#", "##") |
170 | - local color = colors.WHITE | |
171 | - if e.status == 'dev' then color = colors.CRIMSON | |
172 | - elseif e.status == 'mod' then color = colors.GOLD | |
173 | - elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
174 | - elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
181 | + local color, uname = self:getUserColor(e) | |
175 | 182 | |
176 | 183 | self.channels[e.channel] = self.channels[e.channel] or {users={}, log={}} |
177 | 184 | if profile and profile.auth and profile.auth.name then |
... | ... | @@ -180,21 +187,17 @@ function _M:event(e) |
180 | 187 | e.msg = e.msg:sub(1, ni - 1).."#YELLOW##{underline}#"..profile.auth.name.."#{normal}##LAST#"..e.msg:sub(nj + 1) |
181 | 188 | end |
182 | 189 | end |
183 | - self:addMessage("talk", e.channel, e.login, {e.name, color}, e.msg) | |
190 | + self:addMessage("talk", e.channel, e.login, {uname, color}, e.msg) | |
184 | 191 | |
185 | 192 | if type(game) == "table" and game.logChat and self.cur_channel == e.channel then |
186 | 193 | game.logChat("#YELLOW#<%s> %s", e.name, e.msg) |
187 | 194 | end |
188 | 195 | elseif e.se == "Whisper" then |
189 | 196 | e.msg = e.msg:removeColorCodes() |
190 | - local color = colors.WHITE | |
191 | - if e.status == 'dev' then color = colors.CRIMSON | |
192 | - elseif e.status == 'mod' then color = colors.GOLD | |
193 | - elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
194 | - elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
197 | + local color, uname = self:getUserColor(e) | |
195 | 198 | |
196 | 199 | self.channels[self.cur_channel] = self.channels[self.cur_channel] or {users={}, log={}} |
197 | - self:addMessage("whisper", self.cur_channel, e.login, e.name, e.msg) | |
200 | + self:addMessage("whisper", self.cur_channel, e.login, uname, e.msg) | |
198 | 201 | |
199 | 202 | if type(game) == "table" and game.logChat then |
200 | 203 | game.logChat("#GOLD#<Whisper from %s> %s", e.name, e.msg) |
... | ... | @@ -202,11 +205,7 @@ function _M:event(e) |
202 | 205 | e.channel = self.cur_channel |
203 | 206 | elseif e.se == "Achievement" then |
204 | 207 | e.msg = e.msg:removeColorCodes() |
205 | - local color = colors.WHITE | |
206 | - if e.status == 'dev' then color = colors.CRIMSON | |
207 | - elseif e.status == 'mod' then color = colors.GOLD | |
208 | - elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
209 | - elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
208 | + local color, uname = self:getUserColor(e) | |
210 | 209 | |
211 | 210 | local kind = "achievement_other" |
212 | 211 | if e.first then kind = "achievement_first" |
... | ... | @@ -220,18 +219,13 @@ function _M:event(e) |
220 | 219 | if e.first then first = " for the #FIREBRICK#first time!" end |
221 | 220 | |
222 | 221 | self.channels[e.channel] = self.channels[e.channel] or {users={}, log={}} |
223 | - self:addMessage(kind, e.channel, e.login, {e.name, color}, "#{italic}##"..acolor.."#has earned the achievement <"..e.msg..">"..first.."#{normal}#", nil, true) | |
222 | + self:addMessage(kind, e.channel, e.login, {uname, color}, "#{italic}##"..acolor.."#has earned the achievement <"..e.msg..">"..first.."#{normal}#", nil, true) | |
224 | 223 | |
225 | 224 | if type(game) == "table" and game.logChat and self.cur_channel == e.channel then |
226 | 225 | game.logChat("#"..acolor.."#%s has earned the achievement <%s>%s", e.name, e.msg, first) |
227 | 226 | end |
228 | 227 | elseif e.se == "SerialData" then |
229 | - local color = colors.WHITE | |
230 | - if e.status == 'dev' then color = colors.CRIMSON | |
231 | - elseif e.status == 'mod' then color = colors.GOLD | |
232 | - elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
233 | - elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
234 | - e.color_name = color | |
228 | + local color, uname = self:getUserColor(e) | |
235 | 229 | |
236 | 230 | local data = zlib.decompress(e.msg) |
237 | 231 | if not data then return end |
... | ... | @@ -259,8 +253,6 @@ Again, thank you, and enjoy Eyal! |
259 | 253 | |
260 | 254 | #{italic}#Your malevolent local god of darkness, #GOLD#DarkGod#{normal}#]]):format(data.donated, data.donated * 10, data.items_vault_slots) |
261 | 255 | Dialog:simpleLongPopup("Thank you!", text, 600) |
262 | - elseif self.uc_ext then | |
263 | - self.uc_ext:event(e) | |
264 | 256 | end |
265 | 257 | elseif e.se == "SelfJoin" then |
266 | 258 | self:addMessage("join", e.channel, profile.auth.login, e.channel, "#{italic}#Joined channel#{normal}#", nil, true) |
... | ... | @@ -272,58 +264,42 @@ Again, thank you, and enjoy Eyal! |
272 | 264 | self:updateChanList() |
273 | 265 | self:saveChannels() |
274 | 266 | elseif e.se == "Join" then |
275 | - local color = colors.WHITE | |
276 | - if e.status == 'dev' then color = colors.CRIMSON | |
277 | - elseif e.status == 'mod' then color = colors.GOLD | |
278 | - elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
279 | - elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
267 | + local color, uname = self:getUserColor(e) | |
280 | 268 | |
281 | 269 | self.channels[e.channel] = self.channels[e.channel] or {users={}, log={}} |
282 | 270 | self.channels[e.channel].users[e.login] = {name=e.name, donator=e.donator, status=e.status, login=e.login} |
283 | 271 | self.channels_changed = true |
284 | - self:addMessage("join", e.channel, e.login, {e.name, color}, "#{italic}##FIREBRICK#has joined the channel#{normal}#", nil, true) | |
272 | + self:addMessage("join", e.channel, e.login, {uname, color}, "#{italic}##FIREBRICK#has joined the channel#{normal}#", nil, true) | |
285 | 273 | if type(game) == "table" and game.logChat and e.channel == self.cur_channel then |
286 | 274 | game.logChat("#{italic}##FIREBRICK#%s has joined channel %s (press space to talk).#{normal}#", e.login, e.channel) |
287 | 275 | end |
288 | 276 | self:updateChanList() |
289 | 277 | elseif e.se == "Part" then |
290 | - local color = colors.WHITE | |
291 | - if e.status == 'dev' then color = colors.CRIMSON | |
292 | - elseif e.status == 'mod' then color = colors.GOLD | |
293 | - elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
294 | - elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
278 | + local color, uname = self:getUserColor(e) | |
295 | 279 | |
296 | 280 | self.channels[e.channel] = self.channels[e.channel] or {users={}, log={}} |
297 | 281 | self.channels[e.channel].users[e.login] = nil |
298 | 282 | self.channels_changed = true |
299 | - self:addMessage("join", e.channel, e.login, {e.name, color}, "#{italic}##FIREBRICK#has left the channel#{normal}#", nil, true) | |
283 | + self:addMessage("join", e.channel, e.login, {uname, color}, "#{italic}##FIREBRICK#has left the channel#{normal}#", nil, true) | |
300 | 284 | if type(game) == "table" and game.logChat and e.channel == self.cur_channel then |
301 | 285 | game.logChat("#{italic}##FIREBRICK#%s has left channel %s.#{normal}#", e.login, e.channel) |
302 | 286 | end |
303 | 287 | self:updateChanList() |
304 | 288 | elseif e.se == "FriendJoin" then |
305 | - local color = colors.WHITE | |
306 | - if e.status == 'dev' then color = colors.CRIMSON | |
307 | - elseif e.status == 'mod' then color = colors.GOLD | |
308 | - elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
309 | - elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
289 | + local color, uname = self:getUserColor(e) | |
310 | 290 | |
311 | 291 | self.friends[e.login] = {name=e.name, donator=e.donator, status=e.status, login=e.login} |
312 | 292 | if not e.silent then |
313 | - self:addMessage("friendjoin", self.cur_channel, e.login, {e.name, color}, "#{italic}##YELLOW_GREEN#has logged in#{normal}#", nil, true) | |
293 | + self:addMessage("friendjoin", self.cur_channel, e.login, {uname, color}, "#{italic}##YELLOW_GREEN#has logged in#{normal}#", nil, true) | |
314 | 294 | if type(game) == "table" and game.logChat then |
315 | 295 | game.logChat("#{italic}##YELLOW_GREEN#%s has logged in (press space to talk).#{normal}#", e.login, e.channel) |
316 | 296 | end |
317 | 297 | end |
318 | 298 | elseif e.se == "FriendPart" then |
319 | - local color = colors.WHITE | |
320 | - if e.status == 'dev' then color = colors.CRIMSON | |
321 | - elseif e.status == 'mod' then color = colors.GOLD | |
322 | - elseif e.donator == "oneshot" then color = colors.LIGHT_GREEN | |
323 | - elseif e.donator == "recurring" then color = colors.ROYAL_BLUE end | |
299 | + local color, uname = self:getUserColor(e) | |
324 | 300 | |
325 | 301 | self.friends[e.login] = nil |
326 | - self:addMessage("friendpart", self.cur_channel, e.login, {e.name, color}, "#{italic}##CRIMSON#has logged off#{normal}#", nil, true) | |
302 | + self:addMessage("friendpart", self.cur_channel, e.login, {uname, color}, "#{italic}##CRIMSON#has logged off#{normal}#", nil, true) | |
327 | 303 | if type(game) == "table" and game.logChat then |
328 | 304 | game.logChat("#{italic}##CRIMSON#%s has logged off.#{normal}#", e.login, e.channel) |
329 | 305 | end |
... | ... | @@ -351,6 +327,10 @@ Again, thank you, and enjoy Eyal! |
351 | 327 | self.channels_changed = true |
352 | 328 | end |
353 | 329 | |
330 | + if self.uc_ext then | |
331 | + self.uc_ext:event(e) | |
332 | + end | |
333 | + | |
354 | 334 | for fct, _ in pairs(self.on_event) do |
355 | 335 | fct(e) |
356 | 336 | end | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -769,7 +769,8 @@ function _M:addEntity(level, e, typ, x, y, no_added) |
769 | 769 | if x and y then level.map(x, y, Map.TRIGGER, e) end |
770 | 770 | end |
771 | 771 | e:check("addedToLevel", level, x, y) |
772 | - e:check("on_added", level, x, y) | |
772 | + e:check("on_added", level, x, y) -- Sustains are activated here | |
773 | + e:check("on_added_final", level, x, y) | |
773 | 774 | end |
774 | 775 | |
775 | 776 | --- If we are loaded we need a new uid |
... | ... | @@ -811,6 +812,7 @@ function _M:load(dynamic) |
811 | 812 | data = dynamic |
812 | 813 | ret = false |
813 | 814 | for k, e in pairs(data) do self[k] = e end |
815 | + self:onLoadZoneFile(false) | |
814 | 816 | self:triggerHook{"Zone:create", dynamic=dynamic} |
815 | 817 | if self.on_loaded then self:on_loaded() end |
816 | 818 | else | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -250,7 +250,7 @@ end) |
250 | 250 | -- this requires the ActorFOV interface, or an interface that provides self.fov.actors* |
251 | 251 | newAI("target_simple", function(self) |
252 | 252 | -- keep same (hostile) target 90% of the time |
253 | - if self.ai_target.actor and not self.ai_target.actor.dead and self:reactionToward(act) < 0 and rng.percent(90) then return true end | |
253 | + if self.ai_target.actor and not self.ai_target.actor.dead and self:reactionToward(self.ai_target.actor) < 0 and rng.percent(90) then return true end | |
254 | 254 | |
255 | 255 | -- Find closer enemy and target it |
256 | 256 | -- Get list of actors ordered by distance | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -278,11 +278,14 @@ function _M:cloneFull(post_copy) |
278 | 278 | end |
279 | 279 | |
280 | 280 | --- Automatically called by cloneCustom() |
281 | -local function cloneCustomRecurs(clonetable, d, noclonecall, use_saveinstead, alt_nodes) | |
281 | +local function cloneCustomRecurs(clonetable, d, noclonecall, use_saveinstead, alt_nodes, type_checker, first) | |
282 | 282 | if use_saveinstead and (d.__ATOMIC or d.__CLASSNAME) and d.__SAVEINSTEAD then |
283 | 283 | d = d.__SAVEINSTEAD |
284 | 284 | if clonetable[d] then return d, 1 end |
285 | 285 | end |
286 | + if not first and type_checker and d.__CLASSNAME then | |
287 | + if not type_checker(d) then return d, 1 end | |
288 | + end | |
286 | 289 | |
287 | 290 | local nb = 0 |
288 | 291 | local add |
... | ... | @@ -313,11 +316,11 @@ local function cloneCustomRecurs(clonetable, d, noclonecall, use_saveinstead, al |
313 | 316 | if ne_alt ~= nil then ne = ne_alt else ne = e end |
314 | 317 | |
315 | 318 | if clonetable[nk] then nk = clonetable[nk] |
316 | - elseif type(nk) == "table" then nk, add = cloneCustomRecurs(clonetable, nk, noclonecall, use_saveinstead, alt_nodes) nb = nb + add | |
319 | + elseif type(nk) == "table" then nk, add = cloneCustomRecurs(clonetable, nk, noclonecall, use_saveinstead, alt_nodes, type_checker, false) nb = nb + add | |
317 | 320 | end |
318 | 321 | |
319 | 322 | if clonetable[ne] then ne = clonetable[ne] |
320 | - elseif type(ne) == "table" and (type(nk) ~= "string" or nk ~= "__threads") then ne, add = cloneCustomRecurs(clonetable, ne, noclonecall, use_saveinstead, alt_nodes) nb = nb + add | |
323 | + elseif type(ne) == "table" and (type(nk) ~= "string" or nk ~= "__threads") then ne, add = cloneCustomRecurs(clonetable, ne, noclonecall, use_saveinstead, alt_nodes, type_checker, false) nb = nb + add | |
321 | 324 | end |
322 | 325 | |
323 | 326 | n[nk] = ne |
... | ... | @@ -340,11 +343,12 @@ end |
340 | 343 | -- @ or nil to use the default name/ref as keys on the clone |
341 | 344 | -- @ v = the value to assign for instances of this node, |
342 | 345 | -- @ or nil to use the default assignment value |
346 | +-- @param[type=function] type_checker Optional, a function that returns true if the given object is to be cloned and false if it is to be referenced | |
343 | 347 | -- @param[type=table] post_copy Optional, a table to be merged (recursively) with the new object after cloning. |
344 | 348 | -- @return a reference to the clone |
345 | -function _M:cloneCustom(alt_nodes, post_copy) | |
349 | +function _M:cloneCustom(alt_nodes, type_checker, post_copy) | |
346 | 350 | local clonetable = {} |
347 | - local n = cloneCustomRecurs(clonetable, self, nil, nil, alt_nodes) | |
351 | + local n = cloneCustomRecurs(clonetable, self, nil, nil, alt_nodes, type_checker, true) | |
348 | 352 | if post_copy then |
349 | 353 | table.merge(n, post_copy, true) |
350 | 354 | end |
... | ... | @@ -389,6 +393,15 @@ function _M:setCurrentHookDir(dir) |
389 | 393 | _current_hook_dir = dir |
390 | 394 | end |
391 | 395 | |
396 | +function _M:loadHooksFile(file) | |
397 | + assert(_current_hook_dir, "loadHooksFile can only be used in hooks/load.lua") | |
398 | + | |
399 | + local f, err = loadfile(_current_hook_dir.."/"..file) | |
400 | + if not f and err then error(err) end | |
401 | + setfenv(f, setmetatable({}, {__index = _G})) | |
402 | + f() | |
403 | +end | |
404 | + | |
392 | 405 | function _M:bindHook(hook, fct) |
393 | 406 | if type(fct) == "string" and _current_hook_dir then |
394 | 407 | local f, err = loadfile(_current_hook_dir..fct..".lua") | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -46,8 +46,8 @@ function _M:init(chat, id, width) |
46 | 46 | |
47 | 47 | self:generateList() |
48 | 48 | |
49 | - self.c_desc = Textzone.new{width=self.iw - 10 - xoff, height=1, auto_height=true, text=self.text.."\n"} | |
50 | - self.c_list = VariableList.new{width=self.iw - 10 - xoff, max_height=game.h * 0.70 - self.c_desc.h, list=self.list, fct=function(item) self:use(item) end, select=function(item) self:select(item) end} | |
49 | + self.c_desc = Textzone.new{font=chat.dialog_text_font, width=self.iw - 10 - xoff, height=1, auto_height=true, text=self.text.."\n"} | |
50 | + self.c_list = VariableList.new{font=chat.dialog_answer_font, width=self.iw - 10 - xoff, max_height=game.h * 0.70 - self.c_desc.h, list=self.list, fct=function(item) self:use(item) end, select=function(item) self:select(item) end} | |
51 | 51 | |
52 | 52 | local uis = { |
53 | 53 | {left=0, top=0, ui=self.c_desc}, | ... | ... |
1 | 1 | -- TE4 - T-Engine 4 |
2 | --- Copyright (C) 2009 - 2017 Nicolas Casalini | |
2 | +-- Copyright (C) 2009 - 2018 Nicolas Casalini | |
3 | 3 | -- |
4 | 4 | -- This program is free software: you can redistribute it and/or modify |
5 | 5 | -- it under the terms of the GNU General Public License as published by |
... | ... | @@ -131,7 +131,7 @@ function _M:onDownload(handlers) |
131 | 131 | local Dialog = require "engine.ui.Dialog" |
132 | 132 | |
133 | 133 | handlers.on_download_request = function(downid, url, file, mime) |
134 | - if mime == "application/t-engine-addon" and self.allow_downloads.addons and url:find("^http://te4%.org/") then | |
134 | + if mime == "application/t-engine-addon" and self.allow_downloads.addons and url:find("^https://te4%.org/") then | |
135 | 135 | local name = file |
136 | 136 | if self._next_download_name and os.time() - self._next_download_name.time <= 3 then name = self._next_download_name.name self._next_download_name = nil end |
137 | 137 | print("Accepting addon download to:", self.dest) |
... | ... | @@ -140,7 +140,7 @@ function _M:onDownload(handlers) |
140 | 140 | game:registerDialog(self.download_dialog) |
141 | 141 | self.view:downloadAction(downid, self.dest) |
142 | 142 | return |
143 | - elseif mime == "application/t-engine-module" and self.allow_downloads.modules and url:find("^http://te4%.org/") then | |
143 | + elseif mime == "application/t-engine-module" and self.allow_downloads.modules and url:find("^https://te4%.org/") then | |
144 | 144 | local name = file |
145 | 145 | if self._next_download_name and os.time() - self._next_download_name.time <= 3 then name = self._next_download_name.name self._next_download_name = nil end |
146 | 146 | print("Accepting module download to:", self.dest) | ... | ... |