* move imageManager code into new imageManager.h/imageManager.cpp
[openc2e.git] / caosVM_flow.cpp
blob574ad84c5f26c3b6cce0f68f9c5305b72e78f61c
1 /*
2 * caosVM_flow.cpp
3 * openc2e
5 * Created by Alyssa Milburn on Sun May 30 2004.
6 * Copyright (c) 2004 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
20 #include "caosVM.h"
21 #include <iostream>
22 #include "openc2e.h"
23 #include "World.h" // enum
24 #include <cmath> // sqrt
25 #include <sstream>
26 #include "AgentHelpers.h"
27 #include "Vehicle.h" // EPAS
28 #include "caosScript.h" // CAOS
30 /**
31 DOIF (command) condition (condition)
32 %status maybe
33 %pragma variants c1 c2 cv c3
34 %pragma stackdelta 0
35 %cost c1,c2 0
37 Part of a DOIF/ELIF/ELSE/ENDI block. Jumps to the next part of the block if condition is false,
38 otherwise continues executing the script.
40 void caosVM::c_DOIF() {
41 // handled elsewhere
44 /**
45 ELIF (command) condition (condition)
46 %pragma variants all
47 %status maybe
48 %pragma stackdelta 0
49 %cost c1,c2 0
51 Part of a DOIF/ELIF/ELSE/ENDI block. If none of the previous DOIF/ELIF conditions have been true, and condition evaluates to true, then the code in the ELIF block is executed.
52 If found outside a DOIF block, it is equivalent to a DOIF. If you take advantage of this behavior, fuzzie is of the opinion that you should be shot.
54 void caosVM::c_ELIF() {
55 // handled elsewhere
59 /**
60 ELSE (command)
61 %status maybe
62 %pragma variants c1 c2 cv c3
63 %cost c1,c2 0
65 Part of a DOIF/ELIF/ELSE/ENDI block. If ELSE is present, it is jumped to when none of the previous DOIF/ELIF conditions are true.
67 void caosVM::c_ELSE() {
68 // handled elsewhere
71 /**
72 ENDI (command)
73 %status maybe
74 %pragma variants c1 c2 cv c3
75 %cost c1,c2 0
77 The end of a DOIF/ELIF/ELSE/ENDI block.
79 void caosVM::c_ENDI() {
80 // TODO: cost in c2e?
83 /**
84 REPS (command) reps (integer)
85 %status maybe
86 %pragma stackdelta 0
87 %pragma variants c1 c2 cv c3
88 %cost c1,c2 0
90 The start of a REPS...REPE loop. The body of the loop will be executed (reps) times.
92 void caosVM::c_REPS() {
93 // Our expression parameter might push on a value that's a pointer
94 // (or otherwise not an integer); deal with it
96 VM_PARAM_INTEGER(n);
97 result.setInt(n+1); // we'll visit the DECJNZ first to handle 0 etc
101 REPE (command)
102 %status maybe
103 %pragma variants c1 c2 cv c3
104 %cost c1,c2 0
106 The end of a REPS...REPE loop.
108 void caosVM::c_REPE() {
109 // handled elsewhere
113 LOOP (command)
114 %status maybe
115 %pragma variants c1 c2 cv c3
116 %cost c1,c2 0
118 The start of a LOOP...EVER or LOOP...UNTL loop.
120 void caosVM::c_LOOP() {
121 // handled elsewhere
125 EVER (command)
126 %status maybe
127 %pragma variants c1 c2 cv c3
129 Jumps back to the matching LOOP, no matter what.
131 void caosVM::c_EVER() {
132 // handled elsewhere
136 UNTL (command) condition (condition)
137 %status maybe
138 %pragma stackdelta 0
139 %pragma variants c1 c2 cv c3
140 %cost c1,c2 0
142 Jumps back to the matching LOOP unless the condition evaluates to true.
144 void caosVM::c_UNTL() {
145 // handled elsewhere
149 GSUB (command) label (label)
150 %pragma stackdelta 0
151 %status maybe
152 %pragma variants c1 c2 cv c3
154 Jumps to a subroutine defined by SUBR with label (label).
156 void caosVM::c_GSUB() {
157 // handled elsewhere
161 SUBR (command) label (label)
162 %status maybe
163 %pragma stackdelta 0
164 %pragma variants c1 c2 cv c3
166 Defines the start of a subroute to be called with GSUB, with label (label).
167 If the command is encountered during execution, it acts like a STOP.
169 void caosVM::c_SUBR() {
170 // handled elsewhere
174 RETN (command)
175 %pragma stackdelta any
176 %status maybe
177 %pragma variants c1 c2 cv c3
179 Returns from a subroutine called with GSUB.
181 void caosVM::c_RETN() {
182 if (callStack.empty())
183 throw creaturesException("RETN with an empty callstack");
184 nip = callStack.back().nip;
185 callStack.back().valueStack.swap(valueStack);
186 callStack.back().valueStack.clear(); // just in case
187 callStack.pop_back();
191 NEXT (command)
192 %status maybe
193 %pragma variants all
194 %cost c1,c2 0
196 The end of an ENUM...NEXT loop.
198 void caosVM::c_NEXT() {
199 targ = owner;
203 ENUM (command) family (integer) genus (integer) species (integer)
204 %status maybe
205 %pragma stackdelta any
206 %pragma variants c1 c2 cv c3
207 %cost c1,c2 0
209 Loops through all agents with the given classifier. 0 on any field is a
210 wildcard. The loop body is terminated by a NEXT.
212 void caosVM::c_ENUM() {
213 VM_VERIFY_SIZE(3)
214 VM_PARAM_INTEGER(species) caos_assert(species >= 0); caos_assert(species <= 65535);
215 VM_PARAM_INTEGER(genus) caos_assert(genus >= 0); caos_assert(genus <= 255);
216 VM_PARAM_INTEGER(family) caos_assert(family >= 0); caos_assert(family <= 255);
218 caosVar nullv; nullv.reset();
219 valueStack.push_back(nullv);
221 for (std::list<boost::shared_ptr<Agent> >::iterator i
222 = world.agents.begin(); i != world.agents.end(); i++) {
223 boost::shared_ptr<Agent> a = (*i);
224 if (!a) continue;
225 if (species && species != a->species) continue;
226 if (genus && genus != a->genus) continue;
227 if (family && family != a->family) continue;
229 caosVar v; v.setAgent(a);
230 valueStack.push_back(v);
235 ESEE (command) family (integer) genus (integer) species (integer)
236 %status maybe
237 %pragma stackdelta any
238 %pragma variants c2 cv c3
240 Simular to ENUM, but iterates through agents visible to OWNR, or visible to TARG in an install script.
241 An agent can be seen if it is within the range set by RNGE, and is visible (this includes the PERM value
242 of walls that lie between them, and, if the agent is a Creature, it not having the 'invisible' attribute).
244 void caosVM::c_ESEE() {
245 VM_VERIFY_SIZE(3)
246 VM_PARAM_INTEGER(species) caos_assert(species >= 0); caos_assert(species <= 65535);
247 VM_PARAM_INTEGER(genus) caos_assert(genus >= 0); caos_assert(genus <= 255);
248 VM_PARAM_INTEGER(family) caos_assert(family >= 0); caos_assert(family <= 255);
250 Agent *seeing;
251 if (owner) seeing = owner; else seeing = targ;
252 valid_agent(seeing);
254 caosVar nullv; nullv.reset();
255 valueStack.push_back(nullv);
257 std::vector<boost::shared_ptr<Agent> > agents = getVisibleList(seeing, family, genus, species);
258 for (std::vector<boost::shared_ptr<Agent> >::iterator i = agents.begin(); i != agents.end(); i++) {
259 caosVar v; v.setAgent(*i);
260 valueStack.push_back(v);
265 ETCH (command) family (integer) genus (integer) species (integer)
266 %pragma stackdelta any
267 %status maybe
268 %pragma variants c2 cv c3
270 Similar to ENUM, but iterates through the agents OWNR is touching, or TARG is touching in an install script.
272 void caosVM::c_ETCH() {
273 VM_VERIFY_SIZE(3)
274 VM_PARAM_INTEGER(species) caos_assert(species >= 0); caos_assert(species <= 65535);
275 VM_PARAM_INTEGER(genus) caos_assert(genus >= 0); caos_assert(genus <= 255);
276 VM_PARAM_INTEGER(family) caos_assert(family >= 0); caos_assert(family <= 255);
278 Agent *touching;
279 if (owner) touching = owner; else touching = targ;
280 valid_agent(touching);
282 caosVar nullv; nullv.reset();
283 valueStack.push_back(nullv);
285 for (std::list<boost::shared_ptr<Agent> >::iterator i
286 = world.agents.begin(); i != world.agents.end(); i++) {
287 boost::shared_ptr<Agent> a = (*i);
288 if (!a) continue;
289 if (species && species != a->species) continue;
290 if (genus && genus != a->genus) continue;
291 if (family && family != a->family) continue;
292 if (a.get() == touching) continue;
294 if (agentsTouching(a.get(), touching)) {
295 caosVar v; v.setAgent(a);
296 valueStack.push_back(v);
302 EPAS (command) family (integer) genus (integer) species (integer)
303 %pragma stackdelta any
304 %status maybe
306 Similar to ENUM, but iterates through the OWNR vehicle's passengers.
308 void caosVM::c_EPAS() {
309 VM_VERIFY_SIZE(3)
310 VM_PARAM_INTEGER(species) caos_assert(species >= 0); caos_assert(species <= 65535);
311 VM_PARAM_INTEGER(genus) caos_assert(genus >= 0); caos_assert(genus <= 255);
312 VM_PARAM_INTEGER(family) caos_assert(family >= 0); caos_assert(family <= 255);
314 caos_assert(owner);
315 Vehicle *v = dynamic_cast<Vehicle *>(owner.get());
316 caos_assert(v);
318 caosVar nullv; nullv.reset();
319 valueStack.push_back(nullv);
321 for (std::vector<AgentRef>::iterator i = v->passengers.begin(); i != v->passengers.end(); i++) {
322 AgentRef a = *i;
323 if (!a) continue; // TODO: hrr
324 if (species && species != a->species) continue;
325 if (genus && genus != a->genus) continue;
326 if (family && family != a->family) continue;
328 caosVar v; v.setAgent(a); valueStack.push_back(v);
333 ECON (command) agent (agent)
334 %pragma stackdelta any
335 %status stub
337 Loops through all the agents in the connective system containing the given agent.
339 void caosVM::c_ECON() {
340 VM_VERIFY_SIZE(3)
341 VM_PARAM_VALIDAGENT(agent)
343 // TODO: should probably implement this (ECON)
345 caosVar nullv; nullv.reset();
346 valueStack.push_back(nullv);
350 CALL (command) script_no (integer) p1 (any) p2 (any)
351 %status maybe
352 %pragma variants c2 cv c3
354 Calls script_no on OWNR, then waits for it to return. The invoked script
355 will inherit the caller's INST setting, but any changes it makes to it will
356 be reversed once it returns - so eg if you call a script when in INST mode,
357 it calls OVER and returns, you'll still be in INST.
359 Script variables (VAxx) will not be preserved - you'll have to use OVxx
360 for any parameters.
362 void caosVM::c_CALL() {
363 VM_PARAM_VALUE(p2)
364 VM_PARAM_VALUE(p1)
365 VM_PARAM_INTEGER(script_no)
367 valid_agent(owner);
368 caos_assert(script_no >= 0 && script_no < 65536);
370 shared_ptr<script> s = owner->findScript(script_no);
371 if (!s) return;
372 caosVM *newvm = world.getVM(owner);
373 newvm->trace = trace;
375 ensure_assert(newvm->fireScript(s, false));
376 newvm->inst = inst;
377 newvm->_p_[0] = p1;
378 newvm->_p_[1] = p2;
379 owner->pushVM(newvm);
380 stop_loop = true;
384 CAOS (string) inline (integer) state_trans (integer) p1 (anything) p2 (anything) commands (string) throws (integer) catches (integer) report (variable)
385 %status maybe
387 Runs commands as caos code immediately. If inline, copy _IT_ VAxx TARG OWNR, etc. If state_trans, copy FROM and
388 OWNR. If an error occurs, it catches it and stuffs it in the report. (XXX: non-conforming)
391 // XXX: exception catching is very broken right now
393 void caosVM::v_CAOS() {
394 // XXX: capture output
395 VM_PARAM_VARIABLE(report)
396 VM_PARAM_INTEGER(catches)
397 VM_PARAM_INTEGER(throws)
398 VM_PARAM_STRING(commands)
399 VM_PARAM_VALUE(p2)
400 VM_PARAM_VALUE(p1)
401 VM_PARAM_INTEGER(state_trans)
402 VM_PARAM_INTEGER(inl)
404 caosVM *sub = world.getVM(NULL);
405 sub->resetCore();
407 if (inl) {
408 for (int i = 0; i < 100; i++)
409 sub->var[i] = var[i];
410 sub->targ = targ;
411 sub->_it_ = _it_;
412 sub->part = part;
413 sub->owner = owner;
414 // sub->from = from;
417 if (state_trans) {
418 sub->owner = owner;
419 // sub->from = from;
422 sub->_p_[0] = p1;
423 sub->_p_[1] = p2;
425 try {
426 std::istringstream iss(commands);
427 std::ostringstream oss;
428 caosScript s("c3", "CAOS command"); // XXX: variant
429 s.parse(iss);
430 s.installScripts();
431 sub->outputstream = &oss;
432 sub->runEntirely(s.installer);
433 result.setString(oss.str());
434 sub->outputstream = 0;
435 } catch (std::exception &e) {
436 sub->outputstream = 0; // very important that this isn't pointing onto dead stack when the VM is freed
438 if (!throws || catches) {
439 report->setString(e.what());
440 result.setString("###");
441 } else {
442 world.freeVM(sub);
443 throw;
447 world.freeVM(sub);
451 /* vim: set noet: */