Fixed, Visual mode: in some cases translucency was ignored on inner top sides of translucent 3d floors.

Internal, Script Editor: moved some setup/auto-completion logic to separate classes.
Updated ZDoom_DECORATE.cfg (A_QuakeEx).
This commit is contained in:
MaxED 2016-04-28 14:02:24 +00:00
parent 24439cd156
commit 411368307f
14 changed files with 753 additions and 541 deletions

View file

@ -163,7 +163,7 @@ keywords
A_PlayerSkinCheck = "state A_PlayerSkinCheck(str state)";
A_SkullPop = "A_SkullPop[(str type = \"BloodySkull\")]";
A_Quake = "A_Quake(int intensity, int duration, int damageradius, int tremorradius[, str sound = \"world/quake\"])";
A_QuakeEx = "A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad[, str sound = \"world/quake\"[, int flags = 0[, float mulwavex = 1.0[, float mulwavey = 1.0[, float mulwavez = 1.0[, int falloff = 0[, int highpoint = 0]]]]]]])";
A_QuakeEx = "A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad[, str sound = \"world/quake\"[, int flags = 0[, float mulwavex = 1.0[, float mulwavey = 1.0[, float mulwavez = 1.0[, int falloff = 0[, int highpoint = 0[, float rollintensity = 0.0[, float rollwave = 0.0]]]]]]]]])";
//Spawn functions
A_TossGib = "A_TossGib";
A_SpawnDebris = "A_SpawnDebris(str type[, bool translation = false[, float horizontal_vel = 1.0[, float vertical_vel = 1.0]]])";

View file

@ -813,6 +813,10 @@
<Compile Include="Data\ColormapImage.cs" />
<Compile Include="Data\CvarsCollection.cs" />
<Compile Include="Data\HiResImage.cs" />
<Compile Include="Data\Scripting\AccScriptHandler.cs" />
<Compile Include="Data\Scripting\DecorateScriptHandler.cs" />
<Compile Include="Data\Scripting\ModeldefScriptHandler.cs" />
<Compile Include="Data\Scripting\ScriptHandler.cs" />
<Compile Include="Data\TEXTURESImage.cs" />
<Compile Include="Data\PK3FileImage.cs" />
<Compile Include="Data\PK3StructuredReader.cs" />
@ -886,6 +890,7 @@
<Compile Include="GZBuilder\Data\ThingCopyData.cs" />
<Compile Include="Rendering\VisualVertexHandle.cs" />
<Compile Include="Geometry\Line3D.cs" />
<Compile Include="Data\Scripting\ScriptHandlerAttribute.cs" />
<Compile Include="ZDoom\Scripting\DecorateParserSE.cs" />
<Compile Include="ZDoom\GldefsParser.cs" />
<Compile Include="ZDoom\MapinfoParser.cs" />

View file

@ -26,7 +26,6 @@ using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.GZBuilder.Data;
using CodeImp.DoomBuilder.ZDoom.Scripting;
using ScintillaNET;
@ -89,10 +88,11 @@ namespace CodeImp.DoomBuilder.Controls
#region ================== Constructor
// Constructor
protected ScriptDocumentTab(ScriptEditorPanel panel)
protected ScriptDocumentTab(ScriptEditorPanel panel, ScriptConfiguration config)
{
// Keep panel
// Keep panel and config
this.panel = panel;
this.config = config; //mxd
// Make the script control
editor = new ScriptEditorControl();
@ -114,8 +114,10 @@ namespace CodeImp.DoomBuilder.Controls
editor.OnTextChanged += editor_TextChanged; //mxd
//mxd. Bind functionbar events
editor.FunctionBar.DropDown += functionbar_DropDown;
editor.FunctionBar.SelectedIndexChanged += functionbar_SelectedIndexChanged;
editor.OnFunctionBarDropDown += functionbar_DropDown;
//mxd. Setup styles
editor.SetupStyles(config);
}
// Disposer
@ -203,6 +205,8 @@ namespace CodeImp.DoomBuilder.Controls
// This changes the script configurations
public virtual void ChangeScriptConfig(ScriptConfiguration newconfig)
{
config = newconfig; //mxd
editor.SetupStyles(newconfig); //mxd
List<CompilerError> errors = UpdateNavigator(); //mxd
if(panel.ActiveTab == this) panel.ShowErrors(errors); //mxd
}
@ -275,131 +279,12 @@ namespace CodeImp.DoomBuilder.Controls
}
//mxd
//TODO: rewrite this using reflection, move UpdateNavigator[Type] to appropriate parsers
internal List<CompilerError> UpdateNavigator()
{
List<CompilerError> result = new List<CompilerError>();
// Just clear the navigator when current tab has no text
if(editor.Text.Length == 0)
{
editor.FunctionBar.Items.Clear();
editor.FunctionBar.Enabled = false;
return result;
}
// Store currently selected item name
string prevtext = editor.FunctionBar.Text;
switch(config.ScriptType)
{
case ScriptType.ACS:
result = UpdateNavigatorAcs(new MemoryStream(editor.GetText()));
break;
case ScriptType.DECORATE:
result = UpdateNavigatorDecorate(new MemoryStream(editor.GetText()));
break;
case ScriptType.MODELDEF:
result = UpdateNavigatorModeldef(new MemoryStream(editor.GetText()));
break;
default: // Unsupported script type. Just clear the items
editor.FunctionBar.Items.Clear();
break;
}
// Put some text in the navigator (but don't actually trigger selection event)
editor.FunctionBar.Enabled = (editor.FunctionBar.Items.Count > 0);
if(editor.FunctionBar.Items.Count > 0)
{
preventchanges = true;
// Put the text back if we still have the corresponding item
if(!string.IsNullOrEmpty(prevtext))
{
foreach(var item in editor.FunctionBar.Items)
{
if(item.ToString() == prevtext)
{
editor.FunctionBar.Text = item.ToString();
break;
}
}
}
// No dice. Use the first item
if(string.IsNullOrEmpty(editor.FunctionBar.Text))
editor.FunctionBar.Text = editor.FunctionBar.Items[0].ToString();
preventchanges = false;
}
return result;
}
//mxd
private List<CompilerError> UpdateNavigatorDecorate(MemoryStream stream)
{
List<CompilerError> result = new List<CompilerError>();
if(stream == null) return result;
editor.FunctionBar.Items.Clear();
DecorateParserSE parser = new DecorateParserSE();
TextResourceData data = new TextResourceData(stream, new DataLocation(), "DECORATE", false);
if(parser.Parse(data, false))
editor.FunctionBar.Items.AddRange(parser.Actors.ToArray());
if(parser.HasError)
result.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine));
return result;
}
//mxd
private List<CompilerError> UpdateNavigatorModeldef(MemoryStream stream)
{
List<CompilerError> result = new List<CompilerError>();
if(stream == null) return result;
editor.FunctionBar.Items.Clear();
ModeldefParserSE parser = new ModeldefParserSE();
TextResourceData data = new TextResourceData(stream, new DataLocation(), "MODELDEF", false);
if(parser.Parse(data, false))
editor.FunctionBar.Items.AddRange(parser.Models.ToArray());
if(parser.HasError)
result.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine));
return result;
}
//mxd
private List<CompilerError> UpdateNavigatorAcs(MemoryStream stream)
{
List<CompilerError> result = new List<CompilerError>();
if(stream == null) return result;
editor.FunctionBar.Items.Clear();
AcsParserSE parser = new AcsParserSE { AddArgumentsToScriptNames = true, IsMapScriptsLump = this is ScriptLumpDocumentTab, IgnoreErrors = true };
TextResourceData data = new TextResourceData(stream, new DataLocation(), (parser.IsMapScriptsLump ? "?SCRIPTS" : Filename), false);
if(parser.Parse(data, false))
{
editor.FunctionBar.Items.AddRange(parser.NamedScripts.ToArray());
editor.FunctionBar.Items.AddRange(parser.NumberedScripts.ToArray());
editor.FunctionBar.Items.AddRange(parser.Functions.ToArray());
}
if(parser.HasError)
result.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine));
return result;
return editor.UpdateNavigator(this);
}
//mxd
//mxd. TODO: remove this
internal ScriptType VerifyScriptType()
{
ScriptTypeParserSE parser = new ScriptTypeParserSE();
@ -549,22 +434,6 @@ namespace CodeImp.DoomBuilder.Controls
editor.GrabFocus();
}
//mxd
private void functionbar_SelectedIndexChanged(object sender, EventArgs e)
{
if(!preventchanges && editor.FunctionBar.SelectedItem is ScriptItem)
{
ScriptItem si = (ScriptItem)editor.FunctionBar.SelectedItem;
editor.EnsureLineVisible(editor.LineFromPosition(si.CursorPosition));
editor.SelectionStart = si.CursorPosition;
editor.SelectionEnd = si.CursorPosition;
// Focus to the editor!
editor.Focus();
editor.GrabFocus();
}
}
//mxd
private void functionbar_DropDown(object sender, EventArgs e)
{

View file

@ -57,7 +57,6 @@ namespace CodeImp.DoomBuilder.Controls
this.scriptedit.CharAdded += new System.EventHandler<ScintillaNET.CharAddedEventArgs>(this.scriptedit_CharAdded);
this.scriptedit.AutoCCompleted += new System.EventHandler<ScintillaNET.AutoCSelectionEventArgs>(this.scriptedit_AutoCCompleted);
this.scriptedit.InsertCheck += new System.EventHandler<ScintillaNET.InsertCheckEventArgs>(this.scriptedit_InsertCheck);
this.scriptedit.KeyUp += new System.Windows.Forms.KeyEventHandler(this.scriptedit_KeyUp);
this.scriptedit.UpdateUI += new System.EventHandler<ScintillaNET.UpdateUIEventArgs>(this.scriptedit_UpdateUI);
this.scriptedit.KeyDown += new System.Windows.Forms.KeyEventHandler(this.scriptedit_KeyDown);
//
@ -83,6 +82,8 @@ namespace CodeImp.DoomBuilder.Controls
this.functionbar.Size = new System.Drawing.Size(474, 21);
this.functionbar.TabIndex = 2;
this.functionbar.TabStop = false;
this.functionbar.SelectedIndexChanged += new System.EventHandler(this.functionbar_SelectedIndexChanged);
this.functionbar.DropDown += new System.EventHandler(this.functionbar_DropDown);
//
// ScriptEditorControl
//

View file

@ -20,11 +20,13 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data.Scripting;
using CodeImp.DoomBuilder.GZBuilder.Data;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Properties;
@ -53,7 +55,7 @@ namespace CodeImp.DoomBuilder.Controls
#region ================== Enums
// Index for registered images
private enum ImageIndex
internal enum ImageIndex
{
ScriptConstant = 0,
ScriptKeyword = 1,
@ -67,7 +69,6 @@ namespace CodeImp.DoomBuilder.Controls
#region ================== Constants
private const string LEXERS_RESOURCE = "Lexers.cfg";
private const int MAX_BACKTRACK_LENGTH = 200;
private const int HIGHLIGHT_INDICATOR = 8; //mxd. Indicators 0-7 could be in use by a lexer so we'll use indicator 8 to highlight words.
private const string ENTRY_POSITION_MARKER = "[EP]"; //mxd
private const string LINE_BREAK_MARKER = "[LB]"; //mxd
@ -88,6 +89,7 @@ namespace CodeImp.DoomBuilder.Controls
public event FindNextDelegate OnFindNext;
public event FindPreviousDelegate OnFindPrevious; //mxd
public new event EventHandler OnTextChanged; //mxd
public event EventHandler OnFunctionBarDropDown; //mxd
#endregion
@ -95,17 +97,14 @@ namespace CodeImp.DoomBuilder.Controls
// Script configuration
private ScriptConfiguration scriptconfig;
// List of keywords and constants
private List<string> autocompletelist;
//mxd. Handles script type-specific stuff
private ScriptHandler handler;
// Style translation from Scintilla style to ScriptStyleType
private Dictionary<int, ScriptStyleType> stylelookup;
// Current position information
private string curfunctionname = "";
private int curargumentindex;
private int curfunctionstartpos;
private int linenumbercharlength; //mxd. Current max number of chars in the line number
private int lastcaretpos; //mxd. Used in brace matching
private int caretoffset; //mxd. Used to modify caret position after autogenerating stuff
@ -113,6 +112,9 @@ namespace CodeImp.DoomBuilder.Controls
private bool expandcodeblock; //mxd. More gross hacks
private string highlightedword; //mxd
private Encoding encoding; //mxd
//mxd. Event propagation
private bool preventchanges;
#endregion
@ -125,7 +127,6 @@ namespace CodeImp.DoomBuilder.Controls
public string SelectedText { get { return scriptedit.SelectedText; } } //mxd
public bool ShowWhitespace { get { return scriptedit.ViewWhitespace != WhitespaceMode.Invisible; } set { scriptedit.ViewWhitespace = value ? WhitespaceMode.VisibleAlways : WhitespaceMode.Invisible; } }
public bool WrapLongLines { get { return scriptedit.WrapMode != WrapMode.None; } set { scriptedit.WrapMode = (value ? WrapMode.Char : WrapMode.None); } }
public ComboBox FunctionBar { get { return functionbar; } } //mxd
public Scintilla Scintilla { get { return scriptedit; } } //mxd
#endregion
@ -316,11 +317,17 @@ namespace CodeImp.DoomBuilder.Controls
// This sets up the script editor with a script configuration
public void SetupStyles(ScriptConfiguration config)
{
//mxd. Update script handler
handler = General.Types.GetScriptHandler(config.ScriptType);
handler.Initialize(this, config);
//mxd
functionbar.Enabled = (config.ScriptType != ScriptType.UNKNOWN);
Configuration lexercfg = new Configuration();
// Make collections
stylelookup = new Dictionary<int, ScriptStyleType>();
Dictionary<string, string> autocompletedict = new Dictionary<string, string>(StringComparer.Ordinal);
// Keep script configuration
scriptconfig = config;
@ -454,123 +461,9 @@ namespace CodeImp.DoomBuilder.Controls
scriptedit.Styles[stylenum].ForeColor = General.Colors.Colors[colorindex].ToColor();
}
}
// Create the keywords list and apply it
string imageindex = ((int)ImageIndex.ScriptKeyword).ToString(CultureInfo.InvariantCulture);
int keywordsindex = lexercfg.ReadSetting(lexername + ".keywordsindex", -1);
if(keywordsindex > -1)
{
StringBuilder keywordslist = new StringBuilder();
foreach(string k in scriptconfig.Keywords)
{
if(keywordslist.Length > 0) keywordslist.Append(" ");
keywordslist.Append(k);
//mxd. Skip adding the keyword if we have a snippet with the same name
if(!scriptconfig.Snippets.Contains(k))
autocompletedict.Add(k, k + "?" + imageindex);
}
string words = keywordslist.ToString();
scriptedit.SetKeywords(keywordsindex, (scriptconfig.CaseSensitive ? words : words.ToLowerInvariant()));
}
//mxd. Create the properties list and apply it
imageindex = ((int)ImageIndex.ScriptProperty).ToString(CultureInfo.InvariantCulture);
int propertiesindex = lexercfg.ReadSetting(lexername + ".propertiesindex", -1);
if(propertiesindex > -1)
{
StringBuilder propertieslist = new StringBuilder();
HashSet<string> addedprops = new HashSet<string>();
char[] dot = {'.'};
foreach(string p in scriptconfig.Properties)
{
if(propertieslist.Length > 0) propertieslist.Append(" ");
// Scintilla doesn't highlight keywords with '.' or ':', so get rid of those
if(scriptconfig.ScriptType == ScriptType.DECORATE)
{
string prop = p;
if(prop.Contains(":")) prop = prop.Replace(":", string.Empty);
if(prop.Contains("."))
{
// Split dotted properties into separate entries
string[] parts = prop.Split(dot, StringSplitOptions.RemoveEmptyEntries);
List<string> result = new List<string>();
foreach(string part in parts)
{
if(!addedprops.Contains(part))
{
result.Add(part);
addedprops.Add(part);
}
}
if(result.Count > 0) propertieslist.Append(string.Join(" ", result.ToArray()));
}
else
{
addedprops.Add(prop);
propertieslist.Append(prop);
}
}
else
{
propertieslist.Append(p);
}
// Autocomplete doesn't mind '.' or ':'
// Skip adding the keyword if we have a snippet with the same name
if(!scriptconfig.Snippets.Contains(p))
{
if(autocompletedict.ContainsKey(p))
General.ErrorLogger.Add(ErrorType.Warning, "Property \"" + p + "\" is double defined in \"" + scriptconfig.Description + "\" script configuration.");
else
autocompletedict.Add(p, p + "?" + imageindex);
}
}
string words = propertieslist.ToString();
scriptedit.SetKeywords(propertiesindex, (scriptconfig.CaseSensitive ? words : words.ToLowerInvariant()));
}
// Create the constants list and apply it
imageindex = ((int)ImageIndex.ScriptConstant).ToString(CultureInfo.InvariantCulture);
int constantsindex = lexercfg.ReadSetting(lexername + ".constantsindex", -1);
if(constantsindex > -1)
{
StringBuilder constantslist = new StringBuilder();
foreach(string c in scriptconfig.Constants)
{
if(autocompletedict.ContainsKey(c)) continue; //mxd. This happens when there's a keyword and a constant with the same name...
if(constantslist.Length > 0) constantslist.Append(" ");
constantslist.Append(c);
//mxd. Skip adding the constant if we have a snippet with the same name
if(!scriptconfig.Snippets.Contains(c))
autocompletedict.Add(c, c + "?" + imageindex);
}
string words = constantslist.ToString();
scriptedit.SetKeywords(constantsindex, (scriptconfig.CaseSensitive ? words : words.ToLowerInvariant()));
}
//mxd. Create the snippets list and apply it
imageindex = ((int)ImageIndex.ScriptSnippet).ToString(CultureInfo.InvariantCulture);
int snippetindex = lexercfg.ReadSetting(lexername + ".snippetindex", -1);
if(snippetindex > -1 && scriptconfig.Snippets.Count > 0)
{
StringBuilder snippetslist = new StringBuilder();
foreach(string s in scriptconfig.Snippets)
{
if(snippetslist.Length > 0) snippetslist.Append(" ");
snippetslist.Append(s);
autocompletedict.Add(s, s + "?" + imageindex);
}
string words = snippetslist.ToString();
scriptedit.SetKeywords(snippetindex, (scriptconfig.CaseSensitive ? words : words.ToLowerInvariant()));
}
// Make autocomplete list
autocompletelist = new List<string>(autocompletedict.Values);
//mxd. Set keywords
handler.SetKeywords(lexercfg, lexername);
// Setup folding (https://github.com/jacobslusser/ScintillaNET/wiki/Automatic-Code-Folding)
if(General.Settings.ScriptShowFolding && (scriptconfig.Lexer == Lexer.Cpp || scriptconfig.Lexer == Lexer.CppNoCase))
@ -842,141 +735,64 @@ namespace CodeImp.DoomBuilder.Controls
}
}
//mxd
internal List<CompilerError> UpdateNavigator(ScriptDocumentTab tab)
{
List<CompilerError> result = new List<CompilerError>();
// Just clear the navigator when current tab has no text
if(scriptedit.Text.Length == 0)
{
functionbar.Items.Clear();
functionbar.Enabled = false;
return result;
}
// Store currently selected item name
string prevtext = functionbar.Text;
// Repopulate FunctionBar
result = handler.UpdateFunctionBarItems(tab, new MemoryStream(GetText()), functionbar);
// Put some text in the navigator (but don't actually trigger selection event)
functionbar.Enabled = (functionbar.Items.Count > 0);
if(functionbar.Items.Count > 0)
{
preventchanges = true;
// Put the text back if we still have the corresponding item
if(!string.IsNullOrEmpty(prevtext))
{
foreach(var item in functionbar.Items)
{
if(item.ToString() == prevtext)
{
functionbar.Text = item.ToString();
break;
}
}
}
// No dice. Use the first item
if(string.IsNullOrEmpty(functionbar.Text))
functionbar.Text = functionbar.Items[0].ToString();
preventchanges = false;
}
return result;
}
#endregion
#region ================== Utility methods
// This returns the ScriptStyleType for a given Scintilla style
private ScriptStyleType GetScriptStyle(int scintillastyle)
internal ScriptStyleType GetScriptStyle(int scintillastyle)
{
return (stylelookup.ContainsKey(scintillastyle) ? stylelookup[scintillastyle] : ScriptStyleType.PlainText);
}
// This gathers information about the current caret position
private void UpdatePositionInfo()
{
int bracketlevel = 0; // bracket level counting
int argindex = 0; // function argument counting
int pos = scriptedit.CurrentPosition;
// Get the text
string scripttext = scriptedit.Text;
// Reset position info
curfunctionname = "";
curargumentindex = 0;
curfunctionstartpos = 0;
// Determine lowest backtrack position
int limitpos = scriptedit.CurrentPosition - MAX_BACKTRACK_LENGTH;
if(limitpos < 0) limitpos = 0;
// We can only do this when we have function syntax information
if((scriptconfig.ArgumentDelimiter.Length == 0) || (scriptconfig.FunctionClose.Length == 0) ||
(scriptconfig.FunctionOpen.Length == 0) || (scriptconfig.Terminator.Length == 0)) return;
// Get int versions of the function syntax informantion
int argumentdelimiter = scriptconfig.ArgumentDelimiter[0];
int functionclose = scriptconfig.FunctionClose[0];
int functionopen = scriptconfig.FunctionOpen[0];
int terminator = scriptconfig.Terminator[0];
// Continue backtracking until we reached the limitpos
while(pos >= limitpos)
{
// Backtrack 1 character
pos--;
// Get the style and character at this position
ScriptStyleType curstyle = GetScriptStyle(scriptedit.GetStyleAt(pos));
int curchar = scriptedit.GetCharAt(pos);
// Then meeting ) then increase bracket level
// When meeting ( then decrease bracket level
// When bracket level goes -1, then the next word should be the function name
// Only when at bracket level 0, count the comma's for argument index
// TODO:
// Original code checked for scope character here and breaks if found
// Check if in plain text or keyword
if((curstyle == ScriptStyleType.PlainText) || (curstyle == ScriptStyleType.Keyword))
{
// Closing bracket
if(curchar == functionclose)
{
bracketlevel++;
}
// Opening bracket
else if(curchar == functionopen)
{
bracketlevel--;
// Out of the brackets?
if(bracketlevel < 0)
{
// Skip any whitespace before this bracket
do
{
// Backtrack 1 character
curchar = scriptedit.GetCharAt(--pos);
}
while((pos >= limitpos) && ((curchar == ' ') || (curchar == '\t') ||
(curchar == '\r') || (curchar == '\n')));
// NOTE: We may need to set onlyWordCharacters argument in the
// following calls to false to get any argument delimiter included,
// but this may also cause a valid keyword to be combined with other
// surrounding characters that do not belong to the keyword.
// Find the word before this bracket
int wordstart = scriptedit.WordStartPosition(pos, true);
int wordend = scriptedit.WordEndPosition(pos, true);
string word = scripttext.Substring(wordstart, wordend - wordstart);
if(word.Length > 0)
{
// Check if this is an argument delimiter
// I can't remember why I did this, but I'll probably stumble
// upon the problem if this doesn't work right (see note above)
if(word[0] == argumentdelimiter)
{
// We are now in the parent function
bracketlevel++;
argindex = 0;
}
// Now check if this is a keyword
else if(scriptconfig.IsKeyword(word))
{
// Found it!
curfunctionname = scriptconfig.GetKeywordCase(word);
curargumentindex = argindex;
curfunctionstartpos = wordstart;
break;
}
else
{
// Don't know this word
break;
}
}
}
}
// Argument delimiter
else if(curchar == argumentdelimiter)
{
// Only count these at brackt level 0
if(bracketlevel == 0) argindex++;
}
// Terminator
else if(curchar == terminator)
{
// Can't find anything, break now
break;
}
}
}
}
// This registers an image for the autocomplete list
private void RegisterAutoCompleteImage(ImageIndex index, Bitmap image)
{
@ -992,65 +808,6 @@ namespace CodeImp.DoomBuilder.Controls
scriptedit.Markers[(int)index].Symbol = MarkerSymbol.RgbaImage;
}
//mxd. Autocompletion handling (https://github.com/jacobslusser/ScintillaNET/wiki/Basic-Autocompletion)
private bool ShowAutoCompletionList()
{
int currentpos = scriptedit.CurrentPosition;
int wordstartpos = scriptedit.WordStartPosition(currentpos, true);
if(wordstartpos >= currentpos)
{
// Hide the list
scriptedit.AutoCCancel();
return false;
}
// Get entered text
string start = scriptedit.GetTextRange(wordstartpos, currentpos - wordstartpos);
if(string.IsNullOrEmpty(start))
{
// Hide the list
scriptedit.AutoCCancel();
return false;
}
// Don't show Auto-completion list when editing comment, include or string
switch(GetScriptStyle(scriptedit.GetStyleAt(currentpos)))
{
case ScriptStyleType.Comment:
case ScriptStyleType.String:
// Hide the list
scriptedit.AutoCCancel();
return false;
case ScriptStyleType.Include:
// Hide the list unless current word is a keyword
if(!start.StartsWith("#"))
{
scriptedit.AutoCCancel();
return false;
}
break;
}
// Filter the list
List<string> filtered = new List<string>();
foreach(string s in autocompletelist)
if(s.IndexOf(start, StringComparison.OrdinalIgnoreCase) != -1) filtered.Add(s);
// Any matches?
if(filtered.Count > 0)
{
// Show the list
scriptedit.AutoCShow(currentpos - wordstartpos, string.Join(" ", filtered.ToArray()));
return true;
}
// Hide the list
scriptedit.AutoCCancel();
return false;
}
//mxd
private string GetIndentationString(int indent)
{
@ -1216,7 +973,7 @@ namespace CodeImp.DoomBuilder.Controls
if(General.Settings.ScriptAutoShowAutocompletion)
{
// Display the autocompletion list
ShowAutoCompletionList();
handler.ShowAutoCompletionList();
}
}
@ -1419,7 +1176,7 @@ namespace CodeImp.DoomBuilder.Controls
scriptedit.CallTipCancel();
// Show autocomplete
ShowAutoCompletionList();
handler.ShowAutoCompletionList();
skiptextinsert = true;
}
//mxd. Tab to expand code snippet. Do it only when the text cursor is at the end of a keyword.
@ -1449,75 +1206,27 @@ namespace CodeImp.DoomBuilder.Controls
}
}
}
// Key released
private void scriptedit_KeyUp(object sender, KeyEventArgs e)
{
bool showcalltip = false;
int highlightstart = 0;
int highlightend = 0;
UpdatePositionInfo();
// Call tip shown
if(scriptedit.CallTipActive)
{
// Should we hide the call tip?
if(curfunctionname.Length == 0)
{
// Hide the call tip
scriptedit.CallTipCancel();
}
else
{
// Update the call tip
showcalltip = true;
}
}
// No call tip
else
{
// Should we show a call tip?
showcalltip = (curfunctionname.Length > 0) && !scriptedit.AutoCActive;
}
// Show or update call tip
if(showcalltip)
{
string functiondef = scriptconfig.GetFunctionDefinition(curfunctionname);
if(functiondef != null)
{
// Determine the range to highlight
int argsopenpos = functiondef.IndexOf(scriptconfig.FunctionOpen, StringComparison.Ordinal);
int argsclosepos = functiondef.LastIndexOf(scriptconfig.FunctionClose, StringComparison.Ordinal);
if((argsopenpos > -1) && (argsclosepos > -1))
{
string argsstr = functiondef.Substring(argsopenpos + 1, argsclosepos - argsopenpos - 1);
string[] args = argsstr.Split(scriptconfig.ArgumentDelimiter[0]);
if((curargumentindex >= 0) && (curargumentindex < args.Length))
{
int argoffset = 0;
for(int i = 0; i < curargumentindex; i++) argoffset += args[i].Length + 1;
highlightstart = argsopenpos + argoffset + 1;
highlightend = highlightstart + args[curargumentindex].Length;
}
}
//mxd. If the tip obscures the view, move it down
int tippos;
int funcline = scriptedit.LineFromPosition(curfunctionstartpos);
if(scriptedit.CurrentLine > funcline)
tippos = scriptedit.Lines[scriptedit.CurrentLine].Position + scriptedit.Lines[scriptedit.CurrentLine].Indentation; //scriptedit.PositionFromLine(curline) /*+ (curfunctionstartpos - scriptedit.PositionFromLine(funcline))*/;
else
tippos = curfunctionstartpos;
// Show tip
scriptedit.CallTipShow(tippos, functiondef);
scriptedit.CallTipSetHlt(highlightstart, highlightend);
}
//mxd
private void functionbar_SelectedIndexChanged(object sender, EventArgs e)
{
if(!preventchanges && functionbar.SelectedItem is ScriptItem)
{
ScriptItem si = (ScriptItem)functionbar.SelectedItem;
EnsureLineVisible(LineFromPosition(si.CursorPosition));
scriptedit.SelectionStart = si.CursorPosition;
scriptedit.SelectionEnd = si.CursorPosition;
// Focus to the editor!
scriptedit.Focus();
}
}
private void functionbar_DropDown(object sender, EventArgs e)
{
if(OnFunctionBarDropDown != null) OnFunctionBarDropDown(sender, e);
}
#endregion
}
}
}

View file

@ -51,18 +51,15 @@ namespace CodeImp.DoomBuilder.Controls
#region ================== Constructor / Disposer
// Constructor
public ScriptFileDocumentTab(ScriptEditorPanel panel, ScriptConfiguration config) : base(panel)
public ScriptFileDocumentTab(ScriptEditorPanel panel, ScriptConfiguration config) : base(panel, config)
{
string ext = "";
// Initialize
this.filepathname = "";
this.config = config;
editor.SetupStyles(config);
if(config.Extensions.Length > 0) ext = "." + config.Extensions[0];
SetTitle("Untitled" + ext);
editor.ClearUndoRedo();
editor.FunctionBar.Enabled = (config.ScriptType != ScriptType.UNKNOWN); //mxd
}
#endregion
@ -359,12 +356,9 @@ namespace CodeImp.DoomBuilder.Controls
// This changes the script configurations
public override void ChangeScriptConfig(ScriptConfiguration newconfig)
{
this.config = newconfig;
editor.SetupStyles(config);
if(filepathname.Length == 0)
{
string ext = (config.Extensions.Length > 0 ? "." + config.Extensions[0] : "");
string ext = (newconfig.Extensions.Length > 0 ? "." + newconfig.Extensions[0] : "");
SetTitle("Untitled" + ext);
}
else

View file

@ -51,7 +51,7 @@ namespace CodeImp.DoomBuilder.Controls
#region ================== Constructor / Disposer
// Constructor
public ScriptLumpDocumentTab(ScriptEditorPanel panel, string lumpname, ScriptConfiguration config) : base(panel)
public ScriptLumpDocumentTab(ScriptEditorPanel panel, string lumpname, ScriptConfiguration config) : base(panel, config)
{
// Initialize
if(lumpname == MapManager.CONFIG_MAP_HEADER)
@ -65,9 +65,6 @@ namespace CodeImp.DoomBuilder.Controls
this.ismapheader = false;
}
this.config = config;
editor.SetupStyles(config);
// Load the lump data
MemoryStream stream = General.Map.GetLumpData(this.lumpname);
if(stream != null)

View file

@ -0,0 +1,45 @@
#region ================== Namespaces
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Controls;
using CodeImp.DoomBuilder.ZDoom.Scripting;
#endregion
namespace CodeImp.DoomBuilder.Data.Scripting
{
[ScriptHandler(ScriptType.ACS)]
internal class AccScriptHandler : ScriptHandler
{
#region ================== Methods
//TODO: Remove ScriptDocumentTab from here
public override List<CompilerError> UpdateFunctionBarItems(ScriptDocumentTab tab, MemoryStream stream, ComboBox target)
{
List<CompilerError> result = new List<CompilerError>();
if(stream == null) return result;
target.Items.Clear();
AcsParserSE parser = new AcsParserSE { AddArgumentsToScriptNames = true, IsMapScriptsLump = tab is ScriptLumpDocumentTab, IgnoreErrors = true };
TextResourceData data = new TextResourceData(stream, new DataLocation(), (parser.IsMapScriptsLump ? "?SCRIPTS" : tab.Filename), false);
if(parser.Parse(data, false))
{
target.Items.AddRange(parser.NamedScripts.ToArray());
target.Items.AddRange(parser.NumberedScripts.ToArray());
target.Items.AddRange(parser.Functions.ToArray());
}
if(parser.HasError)
result.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine));
return result;
}
#endregion
}
}

View file

@ -0,0 +1,41 @@
#region ================== Namespaces
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Controls;
using CodeImp.DoomBuilder.ZDoom.Scripting;
#endregion
namespace CodeImp.DoomBuilder.Data.Scripting
{
[ScriptHandler(ScriptType.DECORATE)]
internal class DecorateScriptHandler : ScriptHandler
{
#region ================== Methods
//TODO: Remove ScriptDocumentTab from here
public override List<CompilerError> UpdateFunctionBarItems(ScriptDocumentTab tab, MemoryStream stream, ComboBox target)
{
List<CompilerError> result = new List<CompilerError>();
if(stream == null) return result;
target.Items.Clear();
DecorateParserSE parser = new DecorateParserSE();
TextResourceData data = new TextResourceData(stream, new DataLocation(), "DECORATE", false);
if(parser.Parse(data, false))
target.Items.AddRange(parser.Actors.ToArray());
if(parser.HasError)
result.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine));
return result;
}
#endregion
}
}

View file

@ -0,0 +1,41 @@
#region ================== Namespaces
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Controls;
using CodeImp.DoomBuilder.ZDoom.Scripting;
#endregion
namespace CodeImp.DoomBuilder.Data.Scripting
{
[ScriptHandler(ScriptType.MODELDEF)]
internal class ModeldefScriptHandler : ScriptHandler
{
#region ================== Methods
//TODO: Remove ScriptDocumentTab from here
public override List<CompilerError> UpdateFunctionBarItems(ScriptDocumentTab tab, MemoryStream stream, ComboBox target)
{
List<CompilerError> result = new List<CompilerError>();
if(stream == null) return result;
target.Items.Clear();
ModeldefParserSE parser = new ModeldefParserSE();
TextResourceData data = new TextResourceData(stream, new DataLocation(), "MODELDEF", false);
if(parser.Parse(data, false))
target.Items.AddRange(parser.Models.ToArray());
if(parser.HasError)
result.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine));
return result;
}
#endregion
}
}

View file

@ -0,0 +1,442 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Controls;
using CodeImp.DoomBuilder.IO;
using ScintillaNET;
#endregion
namespace CodeImp.DoomBuilder.Data.Scripting
{
[ScriptHandler(ScriptType.UNKNOWN)]
internal class ScriptHandler
{
#region ================== Constants
protected const int MAX_BACKTRACK_LENGTH = 200;
#endregion
#region ================== Variables
private ScriptEditorControl scriptcontrol;
protected Scintilla scriptedit;
protected ScriptConfiguration scriptconfig;
// List of keywords and constants
private List<string> autocompletelist;
// Current position information
private string curfunctionname = "";
private int curargumentindex;
private int curfunctionstartpos;
#endregion
#region ================== Methods
public virtual void Initialize(ScriptEditorControl scriptcontrol, ScriptConfiguration scriptconfig)
{
this.scriptcontrol = scriptcontrol;
this.scriptedit = scriptcontrol.Scintilla;
this.scriptconfig = scriptconfig;
// Bind events
this.scriptedit.KeyUp += scriptedit_KeyUp;
}
//TODO: Remove ScriptDocumentTab from here
public virtual List<CompilerError> UpdateFunctionBarItems(ScriptDocumentTab tab, MemoryStream stream, ComboBox target)
{
// Unsupported script type. Just clear the items
target.Items.Clear();
return new List<CompilerError>();
}
//mxd. Autocompletion handling (https://github.com/jacobslusser/ScintillaNET/wiki/Basic-Autocompletion)
public virtual bool ShowAutoCompletionList()
{
int currentpos = scriptedit.CurrentPosition;
int wordstartpos = scriptedit.WordStartPosition(currentpos, true);
if(wordstartpos >= currentpos)
{
// Hide the list
scriptedit.AutoCCancel();
return false;
}
// Get entered text
string start = scriptedit.GetTextRange(wordstartpos, currentpos - wordstartpos);
if(string.IsNullOrEmpty(start))
{
// Hide the list
scriptedit.AutoCCancel();
return false;
}
// Don't show Auto-completion list when editing comment, include or string
switch(scriptcontrol.GetScriptStyle(scriptedit.GetStyleAt(currentpos)))
{
case ScriptStyleType.Comment:
case ScriptStyleType.String:
// Hide the list
scriptedit.AutoCCancel();
return false;
case ScriptStyleType.Include:
// Hide the list unless current word is a keyword
if(!start.StartsWith("#"))
{
scriptedit.AutoCCancel();
return false;
}
break;
}
// Filter the list
List<string> filtered = new List<string>();
foreach(string s in autocompletelist)
if(s.IndexOf(start, StringComparison.OrdinalIgnoreCase) != -1) filtered.Add(s);
// Any matches?
if(filtered.Count > 0)
{
// Show the list
scriptedit.AutoCShow(currentpos - wordstartpos, string.Join(" ", filtered.ToArray()));
return true;
}
// Hide the list
scriptedit.AutoCCancel();
return false;
}
internal void SetKeywords(Configuration lexercfg, string lexername)
{
Dictionary<string, string> autocompletedict = new Dictionary<string, string>(StringComparer.Ordinal);
// Create the keywords list and apply it
string imageindex = ((int)ScriptEditorControl.ImageIndex.ScriptKeyword).ToString(CultureInfo.InvariantCulture);
int keywordsindex = lexercfg.ReadSetting(lexername + ".keywordsindex", -1);
if(keywordsindex > -1)
{
StringBuilder keywordslist = new StringBuilder();
foreach(string k in scriptconfig.Keywords)
{
if(keywordslist.Length > 0) keywordslist.Append(" ");
keywordslist.Append(k);
//mxd. Skip adding the keyword if we have a snippet with the same name
if(!scriptconfig.Snippets.Contains(k)) autocompletedict.Add(k, k + "?" + imageindex);
}
string words = keywordslist.ToString();
scriptedit.SetKeywords(keywordsindex, (scriptconfig.CaseSensitive ? words : words.ToLowerInvariant()));
}
//mxd. Create the properties list and apply it
imageindex = ((int)ScriptEditorControl.ImageIndex.ScriptProperty).ToString(CultureInfo.InvariantCulture);
int propertiesindex = lexercfg.ReadSetting(lexername + ".propertiesindex", -1);
if(propertiesindex > -1)
{
StringBuilder propertieslist = new StringBuilder();
HashSet<string> addedprops = new HashSet<string>();
char[] dot = { '.' };
foreach(string p in scriptconfig.Properties)
{
if(propertieslist.Length > 0) propertieslist.Append(" ");
// Scintilla doesn't highlight keywords with '.' or ':', so get rid of those
if(scriptconfig.ScriptType == ScriptType.DECORATE)
{
string prop = p;
if(prop.Contains(":")) prop = prop.Replace(":", string.Empty);
if(prop.Contains("."))
{
// Split dotted properties into separate entries
string[] parts = prop.Split(dot, StringSplitOptions.RemoveEmptyEntries);
List<string> result = new List<string>();
foreach(string part in parts)
{
if(!addedprops.Contains(part))
{
result.Add(part);
addedprops.Add(part);
}
}
if(result.Count > 0) propertieslist.Append(string.Join(" ", result.ToArray()));
}
else
{
addedprops.Add(prop);
propertieslist.Append(prop);
}
}
else
{
propertieslist.Append(p);
}
// Autocomplete doesn't mind '.' or ':'
// Skip adding the keyword if we have a snippet with the same name
if(!scriptconfig.Snippets.Contains(p))
{
if(autocompletedict.ContainsKey(p))
General.ErrorLogger.Add(ErrorType.Warning, "Property \"" + p + "\" is double defined in \"" + scriptconfig.Description + "\" script configuration.");
else
autocompletedict.Add(p, p + "?" + imageindex);
}
}
string words = propertieslist.ToString();
scriptedit.SetKeywords(propertiesindex, (scriptconfig.CaseSensitive ? words : words.ToLowerInvariant()));
}
// Create the constants list and apply it
imageindex = ((int)ScriptEditorControl.ImageIndex.ScriptConstant).ToString(CultureInfo.InvariantCulture);
int constantsindex = lexercfg.ReadSetting(lexername + ".constantsindex", -1);
if(constantsindex > -1)
{
StringBuilder constantslist = new StringBuilder();
foreach(string c in scriptconfig.Constants)
{
if(autocompletedict.ContainsKey(c))
continue; //mxd. This happens when there's a keyword and a constant with the same name...
if(constantslist.Length > 0) constantslist.Append(" ");
constantslist.Append(c);
//mxd. Skip adding the constant if we have a snippet with the same name
if(!scriptconfig.Snippets.Contains(c)) autocompletedict.Add(c, c + "?" + imageindex);
}
string words = constantslist.ToString();
scriptedit.SetKeywords(constantsindex, (scriptconfig.CaseSensitive ? words : words.ToLowerInvariant()));
}
//mxd. Create the snippets list and apply it
imageindex = ((int)ScriptEditorControl.ImageIndex.ScriptSnippet).ToString(CultureInfo.InvariantCulture);
int snippetindex = lexercfg.ReadSetting(lexername + ".snippetindex", -1);
if(snippetindex > -1 && scriptconfig.Snippets.Count > 0)
{
StringBuilder snippetslist = new StringBuilder();
foreach(string s in scriptconfig.Snippets)
{
if(snippetslist.Length > 0) snippetslist.Append(" ");
snippetslist.Append(s);
autocompletedict.Add(s, s + "?" + imageindex);
}
string words = snippetslist.ToString();
scriptedit.SetKeywords(snippetindex, (scriptconfig.CaseSensitive ? words : words.ToLowerInvariant()));
}
// Make autocomplete list
autocompletelist = new List<string>(autocompletedict.Values);
}
// This gathers information about the current caret position
protected void UpdatePositionInfo()
{
int bracketlevel = 0; // bracket level counting
int argindex = 0; // function argument counting
int pos = scriptedit.CurrentPosition;
// Get the text
string scripttext = scriptedit.Text;
// Reset position info
curfunctionname = "";
curargumentindex = 0;
curfunctionstartpos = 0;
// Determine lowest backtrack position
int limitpos = Math.Max(0, scriptedit.CurrentPosition - MAX_BACKTRACK_LENGTH);
// We can only do this when we have function syntax information
if((scriptconfig.ArgumentDelimiter.Length == 0) || (scriptconfig.FunctionClose.Length == 0) ||
(scriptconfig.FunctionOpen.Length == 0) || (scriptconfig.Terminator.Length == 0))
return;
// Get int versions of the function syntax informantion
int argumentdelimiter = scriptconfig.ArgumentDelimiter[0];
int functionclose = scriptconfig.FunctionClose[0];
int functionopen = scriptconfig.FunctionOpen[0];
int terminator = scriptconfig.Terminator[0];
// Continue backtracking until we reached the limitpos
while(pos >= limitpos)
{
// Backtrack 1 character
pos--;
// Get the style and character at this position
ScriptStyleType curstyle = scriptcontrol.GetScriptStyle(scriptedit.GetStyleAt(pos));
int curchar = scriptedit.GetCharAt(pos);
// Then meeting ) then increase bracket level
// When meeting ( then decrease bracket level
// When bracket level goes -1, then the next word should be the function name
// Only when at bracket level 0, count the comma's for argument index
// TODO:
// Original code checked for scope character here and breaks if found
// Check if in plain text or keyword
if((curstyle == ScriptStyleType.PlainText) || (curstyle == ScriptStyleType.Keyword))
{
// Closing bracket
if(curchar == functionclose)
{
bracketlevel++;
}
// Opening bracket
else if(curchar == functionopen)
{
bracketlevel--;
// Out of the brackets?
if(bracketlevel < 0)
{
// Skip any whitespace before this bracket
do
{
// Backtrack 1 character
curchar = scriptedit.GetCharAt(--pos);
}
while((pos >= limitpos) && ((curchar == ' ') || (curchar == '\t') ||
(curchar == '\r') || (curchar == '\n')));
// NOTE: We may need to set onlyWordCharacters argument in the
// following calls to false to get any argument delimiter included,
// but this may also cause a valid keyword to be combined with other
// surrounding characters that do not belong to the keyword.
// Find the word before this bracket
int wordstart = scriptedit.WordStartPosition(pos, true);
int wordend = scriptedit.WordEndPosition(pos, true);
string word = scripttext.Substring(wordstart, wordend - wordstart);
if(word.Length > 0)
{
// Check if this is an argument delimiter
// I can't remember why I did this, but I'll probably stumble
// upon the problem if this doesn't work right (see note above)
if(word[0] == argumentdelimiter)
{
// We are now in the parent function
bracketlevel++;
argindex = 0;
}
// Now check if this is a keyword
else if(scriptconfig.IsKeyword(word))
{
// Found it!
curfunctionname = scriptconfig.GetKeywordCase(word);
curargumentindex = argindex;
curfunctionstartpos = wordstart;
break;
}
else
{
// Don't know this word
break;
}
}
}
}
// Argument delimiter
else if(curchar == argumentdelimiter)
{
// Only count these at brackt level 0
if(bracketlevel == 0) argindex++;
}
// Terminator
else if(curchar == terminator)
{
// Can't find anything, break now
break;
}
}
}
}
#endregion
#region ================== Events
private void scriptedit_KeyUp(object sender, KeyEventArgs e)
{
bool showcalltip = false;
int highlightstart = 0;
int highlightend = 0;
UpdatePositionInfo();
// Call tip shown
if(scriptedit.CallTipActive)
{
// Should we hide the call tip?
if(curfunctionname.Length == 0)
{
// Hide the call tip
scriptedit.CallTipCancel();
}
else
{
// Update the call tip
showcalltip = true;
}
}
// No call tip
else
{
// Should we show a call tip?
showcalltip = (curfunctionname.Length > 0) && !scriptedit.AutoCActive;
}
// Show or update call tip
if(showcalltip)
{
string functiondef = scriptconfig.GetFunctionDefinition(curfunctionname);
if(functiondef != null)
{
// Determine the range to highlight
int argsopenpos = functiondef.IndexOf(scriptconfig.FunctionOpen, StringComparison.Ordinal);
int argsclosepos = functiondef.LastIndexOf(scriptconfig.FunctionClose, StringComparison.Ordinal);
if((argsopenpos > -1) && (argsclosepos > -1))
{
string argsstr = functiondef.Substring(argsopenpos + 1, argsclosepos - argsopenpos - 1);
string[] args = argsstr.Split(scriptconfig.ArgumentDelimiter[0]);
if((curargumentindex >= 0) && (curargumentindex < args.Length))
{
int argoffset = 0;
for(int i = 0; i < curargumentindex; i++) argoffset += args[i].Length + 1;
highlightstart = argsopenpos + argoffset + 1;
highlightend = highlightstart + args[curargumentindex].Length;
}
}
//mxd. If the tip obscures the view, move it down
int tippos;
int funcline = scriptedit.LineFromPosition(curfunctionstartpos);
if(scriptedit.CurrentLine > funcline)
tippos = scriptedit.Lines[scriptedit.CurrentLine].Position + scriptedit.Lines[scriptedit.CurrentLine].Indentation; //scriptedit.PositionFromLine(curline) /*+ (curfunctionstartpos - scriptedit.PositionFromLine(funcline))*/;
else
tippos = curfunctionstartpos;
// Show tip
scriptedit.CallTipShow(tippos, functiondef);
scriptedit.CallTipSetHlt(highlightstart, highlightend);
}
}
}
#endregion
}
}

View file

@ -0,0 +1,38 @@
#region ================== Namespaces
using System;
using CodeImp.DoomBuilder.Config;
#endregion
namespace CodeImp.DoomBuilder.Data.Scripting
{
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public sealed class ScriptHandlerAttribute : Attribute
{
#region ================== Variables
private Type type;
private ScriptType scripttype;
#endregion
#region ================== Properties
public Type Type { get { return type; } set { type = value; } }
public ScriptType ScriptType { get { return scripttype; } }
#endregion
#region ================== Constructor / Destructor
// Constructor
public ScriptHandlerAttribute(ScriptType scripttype)
{
// Initialize
this.scripttype = scripttype;
}
#endregion
}
}

View file

@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data.Scripting;
#endregion
@ -34,7 +35,10 @@ namespace CodeImp.DoomBuilder.Types
// List of handler types
private Dictionary<int, TypeHandlerAttribute> handlertypes;
//mxd. List of script handler types
private Dictionary<ScriptType, ScriptHandlerAttribute> scripthandlertypes;
// Disposing
private bool isdisposed;
@ -53,6 +57,7 @@ namespace CodeImp.DoomBuilder.Types
{
// Initialize
handlertypes = new Dictionary<int, TypeHandlerAttribute>();
scripthandlertypes = new Dictionary<ScriptType, ScriptHandlerAttribute>(); //mxd
// Go for all types in this assembly
Type[] types = General.ThisAssembly.GetTypes();
@ -70,6 +75,15 @@ namespace CodeImp.DoomBuilder.Types
attr.Type = tp;
handlertypes.Add(attr.Index, attr);
}
//mxd. Check if class has an ScriptHandler attribute
else if(Attribute.IsDefined(tp, typeof(ScriptHandlerAttribute), false))
{
// Add the type to the list
object[] attribs = tp.GetCustomAttributes(typeof(ScriptHandlerAttribute), false);
ScriptHandlerAttribute attr = (attribs[0] as ScriptHandlerAttribute);
attr.Type = tp;
scripthandlertypes[attr.ScriptType] = attr;
}
}
}
@ -179,8 +193,23 @@ namespace CodeImp.DoomBuilder.Types
public TypeHandlerAttribute GetAttribute(int type)
{
// Do we have a handler type for this?
if(handlertypes.ContainsKey(type)) return handlertypes[type];
else return null;
return (handlertypes.ContainsKey(type) ? handlertypes[type] : null);
}
//mxd
public ScriptHandler GetScriptHandler(ScriptType type)
{
Type t = typeof(ScriptHandler);
// Do we have a handler type for this?
if(scripthandlertypes.ContainsKey(type))
{
t = scripthandlertypes[type].Type;
}
// Create instance
ScriptHandler th = (ScriptHandler)General.ThisAssembly.CreateInstance(t.FullName);
return th;
}
#endregion

View file

@ -112,7 +112,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
texscale = new Vector2D(1.0f / 64.0f, 1.0f / 64.0f);
// Determine brightness
int color = PixelColor.FromInt(level.color).WithAlpha((byte)General.Clamp(level.alpha, 0, 255)).ToInt();
byte alpha = (byte)General.Clamp(level.alpha, 0, 255);
int color = PixelColor.FromInt(level.color).WithAlpha(alpha).ToInt();
int targetbrightness;
if(extrafloor != null && !extrafloor.VavoomType && !level.disablelighting)
{
@ -139,7 +140,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
if(sd.LightLevels[i] == level)
{
if(i > 0) color = sd.LightLevels[i - 1].color;
if(i > 0) color = PixelColor.FromInt(sd.LightLevels[i - 1].color).WithAlpha(alpha).ToInt();
break;
}
}