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.
23 #include "World.h" // enum
24 #include <cmath> // sqrt
26 #include "AgentHelpers.h"
27 #include "Vehicle.h" // EPAS
28 #include "caosScript.h" // CAOS
31 DOIF (command) condition (condition)
33 %pragma variants c1 c2 cv c3
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() {
45 ELIF (command) condition (condition)
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() {
62 %pragma variants c1 c2 cv c3
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() {
74 %pragma variants c1 c2 cv c3
77 The end of a DOIF/ELIF/ELSE/ENDI block.
79 void caosVM::c_ENDI() {
84 REPS (command) reps (integer)
87 %pragma variants c1 c2 cv c3
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
97 result
.setInt(n
+1); // we'll visit the DECJNZ first to handle 0 etc
103 %pragma variants c1 c2 cv c3
106 The end of a REPS...REPE loop.
108 void caosVM::c_REPE() {
115 %pragma variants c1 c2 cv c3
118 The start of a LOOP...EVER or LOOP...UNTL loop.
120 void caosVM::c_LOOP() {
127 %pragma variants c1 c2 cv c3
129 Jumps back to the matching LOOP, no matter what.
131 void caosVM::c_EVER() {
136 UNTL (command) condition (condition)
139 %pragma variants c1 c2 cv c3
142 Jumps back to the matching LOOP unless the condition evaluates to true.
144 void caosVM::c_UNTL() {
149 GSUB (command) label (label)
152 %pragma variants c1 c2 cv c3
154 Jumps to a subroutine defined by SUBR with label (label).
156 void caosVM::c_GSUB() {
161 SUBR (command) label (label)
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() {
175 %pragma stackdelta any
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();
196 The end of an ENUM...NEXT loop.
198 void caosVM::c_NEXT() {
203 ENUM (command) family (integer) genus (integer) species (integer)
205 %pragma stackdelta any
206 %pragma variants c1 c2 cv c3
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() {
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
);
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)
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() {
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);
251 if (owner
) seeing
= owner
; else seeing
= targ
;
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
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() {
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);
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
);
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
306 Similar to ENUM, but iterates through the OWNR vehicle's passengers.
308 void caosVM::c_EPAS() {
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);
315 Vehicle
*v
= dynamic_cast<Vehicle
*>(owner
.get());
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
++) {
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
337 Loops through all the agents in the connective system containing the given agent.
339 void caosVM::c_ECON() {
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)
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
362 void caosVM::c_CALL() {
365 VM_PARAM_INTEGER(script_no
)
368 caos_assert(script_no
>= 0 && script_no
< 65536);
370 shared_ptr
<script
> s
= owner
->findScript(script_no
);
372 caosVM
*newvm
= world
.getVM(owner
);
373 newvm
->trace
= trace
;
375 ensure_assert(newvm
->fireScript(s
, false));
379 owner
->pushVM(newvm
);
384 CAOS (string) inline (integer) state_trans (integer) p1 (anything) p2 (anything) commands (string) throws (integer) catches (integer) report (variable)
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
)
401 VM_PARAM_INTEGER(state_trans
)
402 VM_PARAM_INTEGER(inl
)
404 caosVM
*sub
= world
.getVM(NULL
);
408 for (int i
= 0; i
< 100; i
++)
409 sub
->var
[i
] = var
[i
];
426 std::istringstream
iss(commands
);
427 std::ostringstream oss
;
428 caosScript
s("c3", "CAOS command"); // XXX: variant
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("###");