This repository has been archived by the owner on Dec 2, 2020. It is now read-only.
/
ItemScript.cs
272 lines (214 loc) · 11.2 KB
/
ItemScript.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
using System;
using System.Collections.Generic;
using System.CodeDom.Compiler;
using System.Linq;
using System.Reflection;
using System.Text;
using Bramble.Core;
using Microsoft.CSharp;
using Amaranth.Util;
namespace Amaranth.Engine
{
public class ItemScript
{
/// <summary>
/// Creates a new ItemScript that executes the given C# script when used.
/// </summary>
public static ItemScript Create(string script)
{
// lazy compile it
UncompiledScript uncompiled = new UncompiledScript(script);
sUncompiledScripts.Add(uncompiled);
return uncompiled.Script;
}
public bool Invoke(Entity user, Item item, Action action, Vec? target)
{
// lazy compile if needed
if (mWrapper == null)
{
CompileScripts();
}
return mWrapper.Invoke(user, item, action, target);
}
private static void CompileScripts()
{
string code = GenerateScriptClasses();
string[] referencedAssemblies = new string[]
{
"mscorlib.dll",
"Amaranth.Engine.dll",
"Amaranth.Util.dll"
};
CompilerParameters parameters = new CompilerParameters(referencedAssemblies);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count > 0)
{
Console.WriteLine(code);
foreach (CompilerError error in results.Errors)
{
Console.WriteLine(error.ToString());
}
}
Assembly assembly = results.CompiledAssembly;
// bind the existing ItemUses to their compiled scripts
foreach (UncompiledScript script in sUncompiledScripts)
{
Type type = assembly.GetType("Amaranth.Engine.Compiled." + script.ClassName);
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
ItemScriptWrapper wrapper = (ItemScriptWrapper)constructor.Invoke(new object[0]);
script.Script.mWrapper = wrapper;
}
// all compiled now
sUncompiledScripts.Clear();
}
private static string GenerateScriptClasses()
{
StringBuilder code = new StringBuilder();
code.AppendLine("using System;");
code.AppendLine("using System.Collections.Generic;");
code.AppendLine("using System.Text;");
code.AppendLine("");
code.AppendLine("");
code.AppendLine("namespace Amaranth.Engine.Compiled");
code.AppendLine("{");
foreach (UncompiledScript script in sUncompiledScripts)
{
code.AppendLine(" public class " + script.ClassName + " : ItemScriptWrapper");
code.AppendLine(" {");
code.AppendLine(" protected override bool Use()");
code.AppendLine(" {");
code.AppendLine(" // script:");
code.AppendLine(" " + script.SourceCode);
code.AppendLine("");
code.AppendLine(" return true;");
code.AppendLine(" }");
code.AppendLine(" }");
}
code.AppendLine("}");
return code.ToString();
}
/// <summary>
/// Can only be constructed by ItemUse.Create().
/// </summary>
private ItemScript()
{
}
private class UncompiledScript
{
public string ClassName
{
get { return "ItemScript_" + Index; }
}
public ItemScript Script;
public int Index;
public string SourceCode;
public UncompiledScript(string sourceCode)
{
Index = sNextScriptIndex++;
SourceCode = sourceCode;
// if the code doesn't have an ending ;, add one
if (!SourceCode.EndsWith(";"))
{
SourceCode += ";";
}
Script = new ItemScript();
}
private static int sNextScriptIndex = 1;
}
private static List<UncompiledScript> sUncompiledScripts = new List<UncompiledScript>();
private ItemScriptWrapper mWrapper;
}
public abstract class ItemScriptWrapper
{
public bool Invoke(Entity user, Item item, Action action, Vec? target)
{
mEntity = user;
mItem = item;
mAction = action;
mTarget = target;
return Use();
}
protected abstract bool Use();
#region Base Item uses
protected Item Item { get { return mItem; } }
//### bob: should check type
protected Hero Hero { get { return (Hero)mEntity; } }
protected void GainHealth() { mAction.AddAction(new GainHealthAction(mEntity, Item.Attack)); }
protected void Heal() { mAction.AddAction(new HealAction(mEntity, Item.Attack)); }
protected void HealFull() { mAction.AddAction(new HealFullAction(mEntity)); }
protected void Teleport(int distance) { mAction.AddAction(new TeleportAction(mEntity, distance)); }
protected void MakeTownPortal() { mAction.AddAction(new CreatePortalAction(mEntity)); }
protected void Haste(int boost) { mAction.AddAction(new HasteAction(mEntity, Item.Attack.Roll(), boost)); }
protected void Light(int radius, string noun) { mAction.AddAction(new LightAction(mEntity, new Noun(noun), radius, Item.Attack)); }
protected void Explode(int radius) { mAction.AddAction(new ExplodeAction(mEntity, mItem, radius)); }
protected void DetectFeatures() { mAction.AddAction(new DetectFeaturesAction(mEntity)); }
protected void DetectItems() { mAction.AddAction(new DetectItemsAction(mEntity)); }
protected void CurePoison() { mAction.AddAction(new CurePoisonAction(mEntity)); }
protected void CureDisease() { mAction.AddAction(new CureDiseaseAction(mEntity)); }
protected void RestoreAll() { mAction.AddAction(new RestoreAllAction(Hero)); }
protected void RestoreStrength() { mAction.AddAction(new RestoreAction(Hero, Hero.Stats.Strength)); }
protected void RestoreAgility() { mAction.AddAction(new RestoreAction(Hero, Hero.Stats.Agility)); }
protected void RestoreStamina() { mAction.AddAction(new RestoreAction(Hero, Hero.Stats.Stamina)); }
protected void RestoreWill() { mAction.AddAction(new RestoreAction(Hero, Hero.Stats.Will)); }
protected void RestoreIntellect() { mAction.AddAction(new RestoreAction(Hero, Hero.Stats.Intellect)); }
protected void RestoreCharisma() { mAction.AddAction(new RestoreAction(Hero, Hero.Stats.Charisma)); }
protected void GainStrength() { mAction.AddAction(new GainStatAction(Hero, Hero.Stats.Strength)); }
protected void GainAgility() { mAction.AddAction(new GainStatAction(Hero, Hero.Stats.Agility)); }
protected void GainStamina() { mAction.AddAction(new GainStatAction(Hero, Hero.Stats.Stamina)); }
protected void GainWill() { mAction.AddAction(new GainStatAction(Hero, Hero.Stats.Will)); }
protected void GainIntellect() { mAction.AddAction(new GainStatAction(Hero, Hero.Stats.Intellect)); }
protected void GainCharisma() { mAction.AddAction(new GainStatAction(Hero, Hero.Stats.Charisma)); }
protected void GainAll()
{
GainStrength();
GainAgility();
GainStamina();
GainWill();
GainIntellect();
GainCharisma();
}
protected void SwapStrength() { mAction.AddAction(new SwapStatAction(Hero, Hero.Stats.Strength)); }
protected void SwapAgility() { mAction.AddAction(new SwapStatAction(Hero, Hero.Stats.Agility)); }
protected void SwapStamina() { mAction.AddAction(new SwapStatAction(Hero, Hero.Stats.Stamina)); }
protected void SwapWill() { mAction.AddAction(new SwapStatAction(Hero, Hero.Stats.Will)); }
protected void SwapIntellect() { mAction.AddAction(new SwapStatAction(Hero, Hero.Stats.Intellect)); }
protected void SwapCharisma() { mAction.AddAction(new SwapStatAction(Hero, Hero.Stats.Charisma)); }
protected void Bolt(string noun)
{
if (!mTarget.HasValue) throw new InvalidOperationException("Cannot use the Bolt() script with Items that do not have a target.");
mAction.AddAction(new ElementBoltAction(mEntity, mTarget.Value, new Noun(noun), mItem.Type.Attack));
}
protected void Beam(string noun)
{
if (!mTarget.HasValue) throw new InvalidOperationException("Cannot use the Beam() script with Items that do not have a target.");
mAction.AddAction(new ElementBeamAction(mEntity, mTarget.Value, new Noun(noun), mItem.Type.Attack));
}
protected void Ball(string noun, int radius)
{
if (!mTarget.HasValue) throw new InvalidOperationException("Cannot use the Ball() script with Items that do not have a target.");
mAction.AddAction(new ElementBallAction(mEntity, mTarget.Value, radius, new Noun(noun), mItem.Type.Attack));
}
protected void Cone(string noun, int radius)
{
if (!mTarget.HasValue) throw new InvalidOperationException("Cannot use the Cone() script with Items that do not have a target.");
mAction.AddAction(new ElementConeAction(mEntity, mTarget.Value, radius, new Noun(noun), mItem.Type.Attack));
}
protected void BallSelf(string noun, int radius)
{
//### bob: hack. figure out where the item is
Vec pos = mItem.Position;
if (mAction.Game.Hero.Inventory.Contains(mItem) ||
mAction.Game.Hero.Equipment.Contains(mItem))
{
pos = mAction.Game.Hero.Position;
}
mAction.AddAction(new ElementBallAction(mEntity, pos, radius, new Noun(noun), mItem.Type.Attack));
}
#endregion
private Entity mEntity;
private Item mItem;
private Action mAction;
private Vec? mTarget;
}
}